diff options
author | AlTheKiller <AlTheKiller@svn> | 2009-09-23 01:49:50 +0000 |
---|---|---|
committer | AlTheKiller <AlTheKiller@svn> | 2009-09-23 01:49:50 +0000 |
commit | 45285e8a9300cd754a760560640b75b09f98035e (patch) | |
tree | ad9f093885ad5c98e9dd4156674e7691c22ed0a2 /guilib |
step 3/4: Move linuxport to trunk. How'd I get roped into this?
git-svn-id: https://xbmc.svn.sourceforge.net/svnroot/xbmc/trunk@23097 568bbfeb-2a22-0410-94d2-cc84cf5bfa90
Diffstat (limited to 'guilib')
203 files changed, 62607 insertions, 0 deletions
diff --git a/guilib/AnimatedGif.cpp b/guilib/AnimatedGif.cpp new file mode 100644 index 0000000000..d1c5f6be2b --- /dev/null +++ b/guilib/AnimatedGif.cpp @@ -0,0 +1,648 @@ + +// **************************************************************************** +// +// WINIMAGE.CPP : Generic classes for raster images (MSWindows specialization) +// +// Content: Member definitions for: +// - class CAnimatedGif : Storage class for single images +// - class CAnimatedGifSet : Storage class for sets of images +// +// (Includes routines to Load and Save BMP files and to load GIF files into +// these classes). +// +// -------------------------------------------------------------------------- +// +// Copyright (c) 2000, Juan Soulie <jsoulie@cplusplus.com> +// +// Permission to use, copy, modify, distribute and sell this software or any +// part thereof and/or its documentation for any purpose is granted without fee +// provided that the above copyright notice and this permission notice appear +// in all copies. +// +// This software is provided "as is" without express or implied warranty of +// any kind. The author shall have no liability with respect to the +// infringement of copyrights or patents that any modification to the content +// of this file or this file itself may incur. +// +// **************************************************************************** + +#include "AnimatedGif.h" +#include "FileSystem/SpecialProtocol.h" +#include "utils/EndianSwap.h" + +#ifdef _WIN32PC +extern "C" FILE *fopen_utf8(const char *_Filename, const char *_Mode); +#else +#define fopen_utf8 fopen +#endif + +#pragma pack(1) +// Error processing macro (NO-OP by default): +#define ERRORMSG(PARAM) {} + +#ifndef BI_RGB + #define BI_RGB 0L + #define BI_RLE8 1L + #define BI_RLE4 2L + #define BI_BITFIELDS 3L +#endif + +// Macros to swap data endianness +#define SWAP16(X) X=Endian_SwapLE16(X) +#define SWAP32(X) X=Endian_SwapLE32(X) + +// pre-declaration: +int LZWDecoder (char*, char*, short, int, int, int, const int); + +// **************************************************************************** +// * CAnimatedGif Member definitions * +// **************************************************************************** + +CAnimatedGif::CAnimatedGif() +{ + Height = Width = 0; + Raster = NULL; + Palette = NULL; + pbmi = NULL; + BPP = Transparent = BytesPerRow = 0; + xPos = yPos = Delay = Transparency = 0; + nLoops = 1; //default=play animation 1 time +} + +CAnimatedGif::~CAnimatedGif() +{ + delete [] pbmi; + delete [] Raster; + delete [] Palette; +} + +// Init: Allocates space for raster and palette in GDI-compatible structures. +void CAnimatedGif::Init(int iWidth, int iHeight, int iBPP, int iLoops) +{ + delete[] Raster; + Raster = NULL; + + delete[] pbmi; + pbmi = NULL; + + delete[] Palette; + Palette = NULL; + + // Standard members setup + Transparent = -1; + BytesPerRow = Width = iWidth; + Height = iHeight; + BPP = iBPP; + // Animation Extra members setup: + xPos = yPos = Delay = Transparency = 0; + nLoops = iLoops; + + if (BPP == 24) + { + BytesPerRow *= 3; + pbmi = (GUIBITMAPINFO*)new char [sizeof(GUIBITMAPINFO)]; + } + else + { + pbmi = (GUIBITMAPINFO*)new char[sizeof(GUIBITMAPINFOHEADER)]; + Palette = new COLOR[256]; + } + + BytesPerRow += (ALIGN - Width % ALIGN) % ALIGN; // Align BytesPerRow + int size = BytesPerRow * Height; + + Raster = new char [size]; + + pbmi->bmiHeader.biSize = sizeof (GUIBITMAPINFOHEADER); + pbmi->bmiHeader.biWidth = Width; + pbmi->bmiHeader.biHeight = -Height; // negative means up-to-bottom + pbmi->bmiHeader.biPlanes = 1; + pbmi->bmiHeader.biBitCount = (BPP < 8 ? 8 : BPP); // Our raster is byte-aligned + pbmi->bmiHeader.biCompression = BI_RGB; + pbmi->bmiHeader.biSizeImage = 0; + pbmi->bmiHeader.biXPelsPerMeter = 11811; + pbmi->bmiHeader.biYPelsPerMeter = 11811; + pbmi->bmiHeader.biClrUsed = 0; + pbmi->bmiHeader.biClrImportant = 0; +} + +// operator=: copies an object's content to another +CAnimatedGif& CAnimatedGif::operator = (CAnimatedGif& rhs) +{ + Init(rhs.Width, rhs.Height, rhs.BPP); // respects virtualization + memcpy(Raster, rhs.Raster, BytesPerRow*Height); + memcpy(Palette, rhs.Palette, 256*sizeof(COLOR)); + return *this; +} + + + +CAnimatedGifSet::CAnimatedGifSet() +{ + FrameHeight = FrameWidth = 0; + nLoops = 1; //default=play animation 1 time +} + +CAnimatedGifSet::~CAnimatedGifSet() +{ + Release(); +} + +void CAnimatedGifSet::Release() +{ + FrameWidth = 0; + FrameHeight = 0; + for (int i = 0; i < (int)m_vecimg.size(); ++i) + { + CAnimatedGif* pImage = m_vecimg[i]; + delete pImage; + } + m_vecimg.erase(m_vecimg.begin(), m_vecimg.end()); + +} + +// **************************************************************************** +// * CAnimatedGifSet Member definitions * +// **************************************************************************** + +// AddImage: Adds an image object to the back of the img vector. +void CAnimatedGifSet::AddImage (CAnimatedGif* newimage) +{ + m_vecimg.push_back(newimage); +} + +int CAnimatedGifSet::GetImageCount() const +{ + return m_vecimg.size(); +} + +unsigned char CAnimatedGifSet::getbyte(FILE *fd) +{ + unsigned char uchar; + if (fread(&uchar, 1, 1, fd) == 1) + return uchar; + else + return 0; +} + +// **************************************************************************** +// * LoadGIF * +// * Load a GIF File into the CAnimatedGifSet object * +// * (c) Nov 2000, Juan Soulie <jsoulie@cplusplus.com> * +// **************************************************************************** +int CAnimatedGifSet::LoadGIF (const char * szFileName) +{ + int n; + // Global GIF variables: + int GlobalBPP; // Bits per Pixel. + COLOR * GlobalColorMap; // Global colormap (allocate) + + struct GIFGCEtag + { // GRAPHIC CONTROL EXTENSION + unsigned char BlockSize; // Block Size: 4 bytes + unsigned char PackedFields; // 3.. Packed Fields. Bits detail: + // 0: Transparent Color Flag + // 1: User Input Flag + // 2-4: Disposal Method + unsigned short Delay; // 4..5 Delay Time (1/100 seconds) + unsigned char Transparent; // 6.. Transparent Color Index + } + gifgce; + + struct GIFNetscapeTag + { + unsigned char comment[11]; //4...14 NETSCAPE2.0 + unsigned char SubBlockLength; //15 0x3 + unsigned char reserved; //16 0x1 + unsigned short iIterations ; //17..18 number of iterations (lo-hi) + } + gifnetscape; + + int GraphicExtensionFound = 0; + + // OPEN FILE + FILE *fd = fopen_utf8(_P(szFileName), "rb"); + if (!fd) + { + return 0; + } + + // *1* READ HEADERBLOCK (6bytes) (SIGNATURE + VERSION) + char szSignature[6]; // First 6 bytes (GIF87a or GIF89a) + int iRead = fread(szSignature, 1, 6, fd); + if (iRead != 6) + { + fclose(fd); + return 0; + } + if ( memcmp(szSignature, "GIF", 2) != 0) + { + fclose(fd); + return 0; + } + // *2* READ LOGICAL SCREEN DESCRIPTOR + struct GIFLSDtag + { + unsigned short ScreenWidth; // Logical Screen Width + unsigned short ScreenHeight; // Logical Screen Height + unsigned char PackedFields; // Packed Fields. Bits detail: + // 0-2: Size of Global Color Table + // 3: Sort Flag + // 4-6: Color Resolution + // 7: Global Color Table Flag + unsigned char Background; // Background Color Index + unsigned char PixelAspectRatio; // Pixel Aspect Ratio + } + giflsd; + + iRead = fread(&giflsd, 1, sizeof(giflsd), fd); + if (iRead != sizeof(giflsd)) + { + fclose(fd); + return 0; + } + // endian swap + SWAP16(giflsd.ScreenWidth); + SWAP16(giflsd.ScreenHeight); + + GlobalBPP = (giflsd.PackedFields & 0x07) + 1; + + // fill some animation data: + FrameWidth = giflsd.ScreenWidth; + FrameHeight = giflsd.ScreenHeight; + nLoops = 1; //default=play animation 1 time + + // *3* READ/GENERATE GLOBAL COLOR MAP + GlobalColorMap = new COLOR [1 << GlobalBPP]; + if (giflsd.PackedFields & 0x80) // File has global color map? + for (n = 0;n < 1 << GlobalBPP;n++) + { + GlobalColorMap[n].r = getbyte(fd); + GlobalColorMap[n].g = getbyte(fd); + GlobalColorMap[n].b = getbyte(fd); + } + + else // GIF standard says to provide an internal default Palette: + for (n = 0;n < 256;n++) + GlobalColorMap[n].r = GlobalColorMap[n].g = GlobalColorMap[n].b = n; + + // *4* NOW WE HAVE 3 POSSIBILITIES: + // 4a) Get and Extension Block (Blocks with additional information) + // 4b) Get an Image Separator (Introductor to an image) + // 4c) Get the trailer Char (End of GIF File) + do + { + int charGot = getbyte(fd); + + if (charGot == 0x21) // *A* EXTENSION BLOCK + { + unsigned char extensionType = getbyte(fd); + switch (extensionType) + { + case 0xF9: // Graphic Control Extension + { + if (fread((char*)&gifgce, 1, sizeof(gifgce), fd) == sizeof(gifgce)) + SWAP16(gifgce.Delay); + GraphicExtensionFound++; + getbyte(fd); // Block Terminator (always 0) + } + break; + + case 0xFE: // Comment Extension: Ignored + { + while (int nBlockLength = getbyte(fd)) + for (n = 0;n < nBlockLength;n++) getbyte(fd); + } + break; + + case 0x01: // PlainText Extension: Ignored + { + while (int nBlockLength = getbyte(fd)) + for (n = 0;n < nBlockLength;n++) getbyte(fd); + } + break; + + case 0xFF: // Application Extension: Ignored + { + int nBlockLength = getbyte(fd); + if (nBlockLength == 0x0b) + { + struct GIFNetscapeTag tag; + if (fread((char*)&tag, 1, sizeof(gifnetscape), fd) == sizeof(gifnetscape)) + { + SWAP16(tag.iIterations); + nLoops = tag.iIterations; + } + else + nLoops = 0; + + if (nLoops) nLoops++; + getbyte(fd); + } + else + { + do + { + for (n = 0;n < nBlockLength;n++) getbyte(fd); + } + while ((nBlockLength = getbyte(fd)) != 0); + } + } + break; + + default: // Unknown Extension: Ignored + { + // read (and ignore) data sub-blocks + while (int nBlockLength = getbyte(fd)) + for (n = 0;n < nBlockLength;n++) getbyte(fd); + } + break; + } + } + else if (charGot == 0x2c) + { // *B* IMAGE (0x2c Image Separator) + // Create a new Image Object: + CAnimatedGif* NextImage = new CAnimatedGif(); + + // Read Image Descriptor + struct GIFIDtag + { + unsigned short xPos; // Image Left Position + unsigned short yPos; // Image Top Position + unsigned short Width; // Image Width + unsigned short Height; // Image Height + unsigned char PackedFields; // Packed Fields. Bits detail: + // 0-2: Size of Local Color Table + // 3-4: (Reserved) + // 5: Sort Flag + // 6: Interlace Flag + // 7: Local Color Table Flag + } + gifid; + + memset(&gifid, 0, sizeof(gifid)); + + int LocalColorMap = 0; + if (fread((char*)&gifid, 1, sizeof(gifid), fd) == sizeof(gifid)) + { + SWAP16(gifid.xPos); + SWAP16(gifid.yPos); + SWAP16(gifid.Width); + SWAP16(gifid.Height); + + LocalColorMap = (gifid.PackedFields & 0x08) ? 1 : 0; + } + + NextImage->Init(gifid.Width, gifid.Height, LocalColorMap ? (gifid.PackedFields&7) + 1 : GlobalBPP); + + // Fill NextImage Data + NextImage->xPos = gifid.xPos; + NextImage->yPos = gifid.yPos; + if (GraphicExtensionFound) + { + NextImage->Transparent = (gifgce.PackedFields & 0x01) ? gifgce.Transparent : -1; + NextImage->Transparency = (gifgce.PackedFields & 0x1c) > 1 ? 1 : 0; + NextImage->Delay = gifgce.Delay * 10; + } + + if (NextImage->Transparent != -1) + memset(NextImage->Raster, NextImage->Transparent, NextImage->BytesPerRow * NextImage->Height); + else + memset(NextImage->Raster, giflsd.Background, NextImage->BytesPerRow * NextImage->Height); + + // Read Color Map (if descriptor says so) + size_t palSize = sizeof(COLOR)*(1 << NextImage->BPP); + bool isPalRead = false; + if (LocalColorMap && fread((char*)NextImage->Palette, 1, palSize, fd) == palSize) + isPalRead = true; + + // Copy global, if no palette + if (!isPalRead) + memcpy(NextImage->Palette, GlobalColorMap, palSize); + + short firstbyte = getbyte(fd); // 1st byte of img block (CodeSize) + + // Calculate compressed image block size + // to fix: this allocates an extra byte per block + long ImgStart, ImgEnd; + ImgEnd = ImgStart = ftell(fd); + while ((n = getbyte(fd)) != 0) fseek (fd, ImgEnd += n + 1, SEEK_SET ); + fseek (fd, ImgStart, SEEK_SET); + + // Allocate Space for Compressed Image + char * pCompressedImage = new char [ImgEnd - ImgStart + 4]; + + // Read and store Compressed Image + char * pTemp = pCompressedImage; + while (int nBlockLength = getbyte(fd)) + { + if (fread(pTemp, 1, nBlockLength, fd) != (size_t)nBlockLength) + { + // Error? + } + pTemp += nBlockLength; + } + + // Call LZW/GIF decompressor + n = LZWDecoder( + (char*) pCompressedImage, + (char*) NextImage->Raster, + firstbyte, NextImage->BytesPerRow, //NextImage->AlignedWidth, + gifid.Width, gifid.Height, + ((gifid.PackedFields & 0x40) ? 1 : 0) //Interlaced? + ); + + if (n) + AddImage(NextImage); + else + { + delete NextImage; + ERRORMSG("GIF File Corrupt"); + } + + // Some cleanup + delete[] pCompressedImage; + GraphicExtensionFound = 0; + } + else if (charGot == 0x3b) + { + // *C* TRAILER: End of GIF Info + break; // Ok. Standard End. + } + + } + while ( !feof(fd) ); + + delete[] GlobalColorMap; + fclose(fd); + if ( GetImageCount() == 0) ERRORMSG("Premature End Of File"); + return GetImageCount(); +} + +// **************************************************************************** +// * LZWDecoder (C/C++) * +// * Codec to perform LZW (GIF Variant) decompression. * +// * (c) Nov2000, Juan Soulie <jsoulie@cplusplus.com> * +// **************************************************************************** +// +// Parameter description: +// - bufIn: Input buffer containing a "de-blocked" GIF/LZW compressed image. +// - bufOut: Output buffer where result will be stored. +// - InitCodeSize: Initial CodeSize to be Used +// (GIF files include this as the first byte in a picture block) +// - AlignedWidth : Width of a row in memory (including alignment if needed) +// - Width, Height: Physical dimensions of image. +// - Interlace: 1 for Interlaced GIFs. +// +int LZWDecoder (char * bufIn, char * bufOut, + short InitCodeSize, int AlignedWidth, + int Width, int Height, const int Interlace) +{ + int n; + int row = 0, col = 0; // used to point output if Interlaced + int nPixels, maxPixels; // Output pixel counter + + short CodeSize; // Current CodeSize (size in bits of codes) + short ClearCode; // Clear code : resets decompressor + short EndCode; // End code : marks end of information + + long whichBit; // Index of next bit in bufIn + long LongCode; // Temp. var. from which Code is retrieved + short Code; // Code extracted + short PrevCode; // Previous Code + short OutCode; // Code to output + + // Translation Table: + short Prefix[4096]; // Prefix: index of another Code + unsigned char Suffix[4096]; // Suffix: terminating character + short FirstEntry; // Index of first free entry in table + short NextEntry; // Index of next free entry in table + + unsigned char OutStack[4097]; // Output buffer + int OutIndex; // Characters in OutStack + + int RowOffset; // Offset in output buffer for current row + + // Set up values that depend on InitCodeSize Parameter. + CodeSize = InitCodeSize + 1; + ClearCode = (1 << InitCodeSize); + EndCode = ClearCode + 1; + NextEntry = FirstEntry = ClearCode + 2; + + whichBit = 0; + nPixels = 0; + maxPixels = Width * Height; + RowOffset = 0; + PrevCode = 0; + + while (nPixels < maxPixels) + { + OutIndex = 0; // Reset Output Stack + + // GET NEXT CODE FROM bufIn: + // LZW compression uses code items longer than a single byte. + // For GIF Files, code sizes are variable between 9 and 12 bits + // That's why we must read data (Code) this way: + LongCode = *((long*)(bufIn + whichBit / 8)); // Get some bytes from bufIn + SWAP32(LongCode); + LongCode >>= (whichBit&7); // Discard too low bits + Code = (short)((LongCode & ((1 << CodeSize) - 1) )); // Discard too high bits + whichBit += CodeSize; // Increase Bit Offset + + // SWITCH, DIFFERENT POSIBILITIES FOR CODE: + if (Code == EndCode) // END CODE + break; // Exit LZW Decompression loop + + if (Code == ClearCode) + { + // CLEAR CODE: + CodeSize = InitCodeSize + 1; // Reset CodeSize + NextEntry = FirstEntry; // Reset Translation Table + PrevCode = Code; // Prevent next to be added to table. + continue; // restart, to get another code + } + if (Code < NextEntry) // CODE IS IN TABLE + OutCode = Code; // Set code to output. + + else + { // CODE IS NOT IN TABLE: + OutIndex++; // Keep "first" character of previous output. + OutCode = PrevCode; // Set PrevCode to be output + } + + // EXPAND OutCode IN OutStack + // - Elements up to FirstEntry are Raw-Codes and are not expanded + // - Table Prefices contain indexes to other codes + // - Table Suffices contain the raw codes to be output + while (OutCode >= FirstEntry) + { + if (OutIndex > 4096 || OutCode >= 4096) + return 0; + OutStack[OutIndex++] = Suffix[OutCode]; // Add suffix to Output Stack + OutCode = Prefix[OutCode]; // Loop with preffix + } + + // NOW OutCode IS A RAW CODE, ADD IT TO OUTPUT STACK. + if (OutIndex > 4096) + return 0; + OutStack[OutIndex++] = (unsigned char) OutCode; + + // ADD NEW ENTRY TO TABLE (PrevCode + OutCode) + // (EXCEPT IF PREVIOUS CODE WAS A CLEARCODE) + if (PrevCode != ClearCode) + { + Prefix[NextEntry] = PrevCode; + Suffix[NextEntry] = (unsigned char) OutCode; + NextEntry++; + + // Prevent Translation table overflow: + if (NextEntry >= 4096) + return 0; + + // INCREASE CodeSize IF NextEntry IS INVALID WITH CURRENT CodeSize + if (NextEntry >= (1 << CodeSize)) + { + if (CodeSize < 12) CodeSize++; + else + { + ; + } // Do nothing. Maybe next is Clear Code. + } + } + + PrevCode = Code; + + // Avoid the possibility of overflow on 'bufOut'. + if (nPixels + OutIndex > maxPixels) OutIndex = maxPixels - nPixels; + + // OUTPUT OutStack (LAST-IN FIRST-OUT ORDER) + for (n = OutIndex - 1; n >= 0; n--) + { + if (col == Width) // Check if new row. + { + if (Interlace) + { + // If interlaced:: + if ((row&7) == 0) {row += 8; if (row >= Height) row = 4;} + else if ((row&3) == 0) {row += 8; if (row >= Height) row = 2;} + else if ((row&1) == 0) {row += 4; if (row >= Height) row = 1;} + else row += 2; + } + else // If not interlaced: + row++; + + RowOffset = row * AlignedWidth; // Set new row offset + col = 0; + } + bufOut[RowOffset + col] = OutStack[n]; // Write output + col++; nPixels++; // Increase counters. + } + + } // while (main decompressor loop) + + return whichBit; +} + +// Refer to WINIMAGE.TXT for copyright and patent notices on GIF and LZW. + +#pragma pack() diff --git a/guilib/AnimatedGif.h b/guilib/AnimatedGif.h new file mode 100644 index 0000000000..4cfc322b87 --- /dev/null +++ b/guilib/AnimatedGif.h @@ -0,0 +1,162 @@ +/*! +\file AnimatedGif.h +\brief +*/ + + +// **************************************************************************** +// +// WINIMAGE.H : Generic classes for raster images (MSWindows specialization) +// +// Content: Class declarations of: +// - class CAnimatedGif : Storage class for single images +// - class CAnimatedGifSet : Storage class for sets of images +// - class C_AnimationWindow : Window Class to display animations +// +// (Includes declarations of routines to Load and Save BMP files and to load +// GIF files into these classes). +// +// -------------------------------------------------------------------------- +// +// Copyright © 2000, Juan Soulie <jsoulie@cplusplus.com> +// +// Permission to use, copy, modify, distribute and sell this software or any +// part thereof and/or its documentation for any purpose is granted without fee +// provided that the above copyright notice and this permission notice appear +// in all copies. +// +// This software is provided "as is" without express or implied warranty of +// any kind. The author shall have no liability with respect to the +// infringement of copyrights or patents that any modification to the content +// of this file or this file itself may incur. +// +// **************************************************************************** + + +#include "Texture.h" // for COLOR + +#pragma pack(1) + +#undef ALIGN +#define ALIGN sizeof(int) ///< Windows GDI expects all int-aligned + +/*! + \ingroup textures + \brief + */ +typedef struct tagGUIRGBQUAD +{ + BYTE rgbBlue; + BYTE rgbGreen; + BYTE rgbRed; + BYTE rgbReserved; +} +GUIRGBQUAD; + +/*! + \ingroup textures + \brief + */ +typedef struct tagGUIBITMAPINFOHEADER +{ + DWORD biSize; + LONG biWidth; + LONG biHeight; + WORD biPlanes; + WORD biBitCount; + DWORD biCompression; + DWORD biSizeImage; + LONG biXPelsPerMeter; + LONG biYPelsPerMeter; + DWORD biClrUsed; + DWORD biClrImportant; +} +GUIBITMAPINFOHEADER; + +/*! + \ingroup textures + \brief + */ +typedef struct tagGUIBITMAPINFO +{ + GUIBITMAPINFOHEADER bmiHeader; + GUIRGBQUAD bmiColors[1]; +} GUIBITMAPINFO; + +#pragma pack() + + +// **************************************************************************** +// * CAnimatedGif * +// * Storage class for single images * +// **************************************************************************** +/*! + \ingroup textures + \brief Storage class for single images + */ +class CAnimatedGif +{ +public: + CAnimatedGif(); + virtual ~CAnimatedGif(); + + // standard members: + int Width, Height; ///< Dimensions in pixels + int BPP; // Bits Per Pixel + char* Raster; ///< Bits of Raster Data (Byte Aligned) + COLOR* Palette; ///< Color Map + int BytesPerRow; ///< Width (in bytes) including alignment! + int Transparent; ///< Index of Transparent color (-1 for none) + + // Extra members for animations: + int nLoops; + int xPos, yPos; ///< Relative Position + int Delay; ///< Delay after image in 1/1000 seconds. + int Transparency; ///< Animation Transparency. + // Windows GDI specific: + GUIBITMAPINFO* pbmi; ///< BITMAPINFO structure + + // constructor and destructor: + + // operator= (object copy) + CAnimatedGif& operator= (CAnimatedGif& rhs); + + /// \brief Image initializer (allocates space for raster and palette): + void Init (int iWidth, int iHeight, int iBPP, int iLoops = 0); + + inline char& Pixel (int x, int y) { return Raster[y*BytesPerRow + x];} + +}; + +// **************************************************************************** +// * CAnimatedGifSet * +// * Storage class for sets of images * +// **************************************************************************** +/*! + \ingroup textures + \brief Storage class for sets of images + */ +class CAnimatedGifSet +{ +public: + + // constructor and destructor: + CAnimatedGifSet(); + virtual ~CAnimatedGifSet(); + + int FrameWidth, FrameHeight; ///< Dimensions of ImageSet in pixels. + int nLoops; // Number of Loops (0 = infinite) + + std::vector<CAnimatedGif*> m_vecimg; ///< Images' Vector. + + void AddImage (CAnimatedGif*); ///< Append new image to vector (push_back) + + int GetImageCount() const; + // File Formats: + int LoadGIF (const char* szFile); + + void Release(); +protected: + unsigned char getbyte(FILE *fd); +}; + diff --git a/guilib/AudioContext.cpp b/guilib/AudioContext.cpp new file mode 100644 index 0000000000..0c8e8dfe95 --- /dev/null +++ b/guilib/AudioContext.cpp @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" +#include "AudioContext.h" +#include "GUIAudioManager.h" +#include "IAudioDeviceChangedCallback.h" +#include "Settings.h" +#include "GUISettings.h" +#include "XBAudioConfig.h" +#ifdef _WIN32PC +#include "WINDirectSound.h" +#endif +extern HWND g_hWnd; + + +CAudioContext g_audioContext; + +#ifdef _WIN32 +static GUID g_digitaldevice; +BOOL CALLBACK DSEnumCallback( + LPGUID lpGuid, + LPCSTR lpcstrDescription, + LPCSTR lpcstrModule, + LPVOID lpContext +) +{ + if(strstr(lpcstrDescription, "Digital Output") != NULL) + { + g_digitaldevice = *lpGuid; + return false; + } + return true; +} +#endif + +CAudioContext::CAudioContext() +{ + m_bAC3EncoderActive=false; + m_iDevice=DEFAULT_DEVICE; + m_strDevice.clear(); +#ifdef HAS_AUDIO +#ifdef HAS_AUDIO_PASS_THROUGH + m_pAC97Device=NULL; +#endif + m_pDirectSoundDevice=NULL; +#endif +} + +CAudioContext::~CAudioContext() +{ +} + +// \brief Create a new device by type (DEFAULT_DEVICE, DIRECTSOUND_DEVICE, AC97_DEVICE) +// Note: DEFAULT_DEVICE is created by the IAudioDeviceChangedCallback +void CAudioContext::SetActiveDevice(int iDevice) +{ + /* if device is the same, no need to bother */ +#ifdef _WIN32PC + if(m_iDevice == iDevice && g_guiSettings.GetString("audiooutput.audiodevice").Equals(m_strDevice)) + { + if (iDevice != NONE && m_pDirectSoundDevice) + { + DSCAPS devCaps = {0}; + devCaps.dwSize = sizeof(devCaps); + if (SUCCEEDED(m_pDirectSoundDevice->GetCaps(&devCaps))) // Make sure the DirectSound interface is still valid. + return; + } + } + +#else + if(m_iDevice == iDevice) + return; +#endif + + if (iDevice==DEFAULT_DEVICE) + { + /* we just tell callbacks to init, it will setup audio */ + g_audioManager.Initialize(iDevice); + return; + } + + /* deinit current device */ + RemoveActiveDevice(); + + m_iDevice=iDevice; + m_strDevice=g_guiSettings.GetString("audiooutput.audiodevice"); + +#ifdef HAS_AUDIO + memset(&g_digitaldevice, 0, sizeof(GUID)); + if (FAILED(DirectSoundEnumerate(DSEnumCallback, this))) + CLog::Log(LOGERROR, "%s - failed to enumerate output devices", __FUNCTION__); + + if (iDevice==DIRECTSOUND_DEVICE + || iDevice==DIRECTSOUND_DEVICE_DIGITAL) + { + LPGUID guid = NULL; +#ifdef _WIN32PC + CWDSound p_dsound; + std::vector<DSDeviceInfo > deviceList = p_dsound.GetSoundDevices(); + std::vector<DSDeviceInfo >::const_iterator iter = deviceList.begin(); + for (int i=0; iter != deviceList.end(); i++) + { + DSDeviceInfo dev = *iter; + + if (g_guiSettings.GetString("audiooutput.audiodevice").Equals(dev.strDescription)) + { + guid = dev.lpGuid; + CLog::Log(LOGDEBUG, "%s - selecting %s as output devices", __FUNCTION__, dev.strDescription.c_str()); + break; + } + + ++iter; + } +#else + if(iDevice == DIRECTSOUND_DEVICE_DIGITAL + && ( g_digitaldevice.Data1 || g_digitaldevice.Data2 + || g_digitaldevice.Data3 || g_digitaldevice.Data4 )) + guid = &g_digitaldevice; +#endif + + // Create DirectSound + if (FAILED(DirectSoundCreate( guid, &m_pDirectSoundDevice, NULL ))) + { + CLog::Log(LOGERROR, "DirectSoundCreate() Failed"); + return; + } + if (FAILED(m_pDirectSoundDevice->SetCooperativeLevel(g_hWnd, DSSCL_PRIORITY))) + { + CLog::Log(LOGERROR, "DirectSoundDevice::SetCooperativeLevel() Failed"); + return; + } + } + else if (iDevice == DIRECTSOUND_DEVICE_DIGITAL) + { + + } +#ifdef HAS_AUDIO_PASS_THROUGH + else if (iDevice==AC97_DEVICE) + { + // Create AC97 Device + if (FAILED(Ac97CreateMediaObject(DSAC97_CHANNEL_DIGITAL, NULL, NULL, &m_pAC97Device))) + { + CLog::Log(LOGERROR, "Failed to create digital Ac97CreateMediaObject()"); + return; + } + } +#endif + // Don't log an error if the caller specifically asked for no device + // externalplayer does this to ensure all audio devices are closed + // during external playback + else if (iDevice != NONE) + { + CLog::Log(LOGERROR, "Failed to create audio device"); + return; + } +#endif + g_audioManager.Initialize(m_iDevice); +} + +// \brief Return the active device type (NONE, DEFAULT_DEVICE, DIRECTSOUND_DEVICE, AC97_DEVICE) +int CAudioContext::GetActiveDevice() +{ + return m_iDevice; +} + +// \brief Remove the current sound device, eg. to setup new speaker config +void CAudioContext::RemoveActiveDevice() +{ + g_audioManager.DeInitialize(m_iDevice); + m_iDevice=NONE; + +#ifdef HAS_AUDIO +#ifdef HAS_AUDIO_PASS_THROUGH + SAFE_RELEASE(m_pAC97Device); +#endif + SAFE_RELEASE(m_pDirectSoundDevice); +#endif +} + +// \brief set a new speaker config +void CAudioContext::SetupSpeakerConfig(int iChannels, bool& bAudioOnAllSpeakers, bool bIsMusic) +{ + m_bAC3EncoderActive = false; + bAudioOnAllSpeakers = false; + +#ifdef HAS_AUDIO + DWORD spconfig = DSSPEAKER_USE_DEFAULT; + if (g_guiSettings.GetInt("audiooutput.mode") == AUDIO_DIGITAL) + { + if (((g_guiSettings.GetBool("musicplayer.outputtoallspeakers")) && (bIsMusic)) || (g_stSettings.m_currentVideoSettings.m_OutputToAllSpeakers && !bIsMusic)) + { + if( g_audioConfig.GetAC3Enabled() ) + { + bAudioOnAllSpeakers = true; + m_bAC3EncoderActive = true; + spconfig = DSSPEAKER_USE_DEFAULT; //Allows ac3 encoder should it be enabled + } + else + { + if (iChannels == 1) + spconfig = DSSPEAKER_MONO; + else + { + spconfig = DSSPEAKER_STEREO; + } + } + } + else + { + if (iChannels == 1) + spconfig = DSSPEAKER_MONO; + else if (iChannels == 2) + spconfig = DSSPEAKER_STEREO; + else + { + spconfig = DSSPEAKER_USE_DEFAULT; //Allows ac3 encoder should it be enabled + m_bAC3EncoderActive = g_audioConfig.GetAC3Enabled(); + } + } + } + else // We don't want to use the Dolby Digital Encoder output. Downmix to surround instead. + { + if (iChannels == 1) + spconfig = DSSPEAKER_MONO; + else + { + // check if surround mode is allowed, if not then use normal stereo + // don't always set it to default as that enabled ac3 encoder if that is allowed in dash + // ruining quality + spconfig = DSSPEAKER_STEREO; + } + } + + DWORD spconfig_old = DSSPEAKER_USE_DEFAULT; + if(m_pDirectSoundDevice) + { + m_pDirectSoundDevice->GetSpeakerConfig(&spconfig_old); + spconfig_old = DSSPEAKER_CONFIG(spconfig_old); + } + + /* speaker config identical, no need to do anything */ + if(spconfig == spconfig_old) return; +#endif + + /* speaker config has changed, caller need to recreate it */ + RemoveActiveDevice(); +} + +bool CAudioContext::IsAC3EncoderActive() const +{ + return m_bAC3EncoderActive; +} + +bool CAudioContext::IsPassthroughActive() const +{ + return (m_iDevice == DIRECTSOUND_DEVICE_DIGITAL); +} + diff --git a/guilib/AudioContext.h b/guilib/AudioContext.h new file mode 100644 index 0000000000..efa62f08da --- /dev/null +++ b/guilib/AudioContext.h @@ -0,0 +1,79 @@ +/*! +\file AudioContext.h +\brief +*/ + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "StdString.h" + +// forward definitions +class IAudioDeviceChangedCallback; + +#define DSMIXBINTYPE_STANDARD 1 +#define DSMIXBINTYPE_DMO 2 +#define DSMIXBINTYPE_AAC 3 +#define DSMIXBINTYPE_OGG 4 +#define DSMIXBINTYPE_CUSTOM 5 +#define DSMIXBINTYPE_STEREOALL 6 +#define DSMIXBINTYPE_STEREOLEFT 7 +#define DSMIXBINTYPE_STEREORIGHT 8 + +class CAudioContext +{ +public: + CAudioContext(); + virtual ~CAudioContext(); + + void SetActiveDevice(int iDevice); + int GetActiveDevice(); + +#ifdef HAS_AUDIO + LPDIRECTSOUND8 GetDirectSoundDevice() { return m_pDirectSoundDevice; } +#ifdef HAS_AUDIO_PASS_THROUGH + LPAC97MEDIAOBJECT GetAc97Device() { return m_pAC97Device; } +#endif +#endif + + void SetupSpeakerConfig(int iChannels, bool& bAudioOnAllSpeakers, bool bIsMusic=true); + bool IsAC3EncoderActive() const; + bool IsPassthroughActive() const; + + enum AUDIO_DEVICE {NONE=0, DEFAULT_DEVICE, DIRECTSOUND_DEVICE, AC97_DEVICE, DIRECTSOUND_DEVICE_DIGITAL }; +protected: + void RemoveActiveDevice(); + +#ifdef HAS_AUDIO +#ifdef HAS_AUDIO_PASS_THROUGH + LPAC97MEDIAOBJECT m_pAC97Device; +#endif + LPDIRECTSOUND8 m_pDirectSoundDevice; +#endif + + int m_iDevice; + CStdString m_strDevice; + bool m_bAC3EncoderActive; +}; + +extern CAudioContext g_audioContext; diff --git a/guilib/DirectXGraphics.cpp b/guilib/DirectXGraphics.cpp new file mode 100644 index 0000000000..5563b51304 --- /dev/null +++ b/guilib/DirectXGraphics.cpp @@ -0,0 +1,458 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "DirectXGraphics.h" +#include "Texture.h" +#include "FileSystem/File.h" + +LPVOID XPhysicalAlloc(SIZE_T s, DWORD ulPhysicalAddress, DWORD ulAlignment, DWORD flProtect) +{ + return malloc(s); +} + +void XPhysicalFree(LPVOID lpAddress) +{ + free(lpAddress); +} + +D3DFORMAT GetD3DFormat(XB_D3DFORMAT format) +{ + switch (format) + { + case XB_D3DFMT_A8R8G8B8: + case XB_D3DFMT_LIN_A8R8G8B8: + return D3DFMT_LIN_A8R8G8B8; + case XB_D3DFMT_DXT1: + return D3DFMT_DXT1; + case XB_D3DFMT_DXT2: + return D3DFMT_DXT2; + case XB_D3DFMT_DXT4: + return D3DFMT_DXT4; + case XB_D3DFMT_P8: + return D3DFMT_LIN_A8R8G8B8; + default: + return D3DFMT_UNKNOWN; + } +} + +DWORD BytesPerPixelFromFormat(XB_D3DFORMAT format) +{ + switch (format) + { + case XB_D3DFMT_A8R8G8B8: + case XB_D3DFMT_LIN_A8R8G8B8: + case XB_D3DFMT_DXT4: + return 4; + case XB_D3DFMT_P8: + case XB_D3DFMT_DXT1: + case XB_D3DFMT_DXT2: + return 1; + default: + return 0; + } +} + +bool IsPalettedFormat(XB_D3DFORMAT format) +{ + if (format == XB_D3DFMT_P8) + return true; + return false; +} + +void ParseTextureHeader(D3DTexture *tex, XB_D3DFORMAT &fmt, DWORD &width, DWORD &height, DWORD &pitch, DWORD &offset) +{ + fmt = (XB_D3DFORMAT)((tex->Format & 0xff00) >> 8); + offset = tex->Data; + if (tex->Size) + { + width = (tex->Size & 0x00000fff) + 1; + height = ((tex->Size & 0x00fff000) >> 12) + 1; + pitch = (((tex->Size & 0xff000000) >> 24) + 1) << 6; + } + else + { + width = 1 << ((tex->Format & 0x00f00000) >> 20); + height = 1 << ((tex->Format & 0x0f000000) >> 24); + pitch = width * BytesPerPixelFromFormat(fmt); + } +} + +bool IsSwizzledFormat(XB_D3DFORMAT format) +{ + switch (format) + { + case XB_D3DFMT_A8R8G8B8: + case XB_D3DFMT_P8: + return true; + default: + return false; + } +} + +#ifdef HAS_DX +HRESULT XGWriteSurfaceToFile(LPDIRECT3DSURFACE9 pSurface, const char *fileName) +#else +HRESULT XGWriteSurfaceToFile(void* pixels, int width, int height, const char *fileName) +#endif +{ +#ifdef HAS_DX + D3DLOCKED_RECT lr; + D3DSURFACE_DESC desc; + pSurface->GetDesc(&desc); + if (S_OK == pSurface->LockRect(&lr, NULL, 0)) + { +#endif + XFILE::CFile file; + if (file.OpenForWrite(fileName, true)) + { + // create a 24bit BMP header + BMPHEAD bh; + memset((char *)&bh,0,sizeof(BMPHEAD)); + memcpy(bh.id,"BM",2); + bh.headersize = 54L; + bh.infoSize = 0x28L; +#ifdef HAS_DX + bh.width = desc.Width; + bh.height = desc.Height; +#else + bh.width = width; + bh.height = height; +#endif + bh.biPlanes = 1; + bh.bits = 24; + bh.biCompression = 0L; + + //number of bytes per line in a BMP is divisible by 4 + long bytesPerLine = bh.width * 3; + if (bytesPerLine & 0x0003) + { + bytesPerLine |= 0x0003; + ++bytesPerLine; + } + // filesize = headersize + bytesPerLine * number of lines + bh.filesize = bh.headersize + bytesPerLine * bh.height; + + file.Write(&bh.id, sizeof(bh)); + + BYTE *lineBuf = new BYTE[bytesPerLine]; + memset(lineBuf, 0, bytesPerLine); + // lines are stored in BMPs upside down +#ifdef HAS_DX + for (UINT y = bh.height; y; --y) +#else + for (UINT y = 1 ; y <= (UINT) bh.height ; ++y) //compensate for gl's inverted Y axis +#endif + { +#ifdef HAS_DX + BYTE *s = (BYTE *)lr.pBits + (y - 1) * lr.Pitch; + BYTE *d = lineBuf; +#else + BYTE *s = (BYTE *)pixels + (y - 1) * 4 * width; + BYTE *d = lineBuf; +#endif + for (UINT x = 0; x < (UINT) bh.width; x++) + { + *d++ = *(s + x * 4); + *d++ = *(s + x * 4 + 1); + *d++ = *(s + x * 4 + 2); + } + file.Write(lineBuf, bytesPerLine); + } + delete[] lineBuf; + file.Close(); + } +#ifdef HAS_DX + pSurface->UnlockRect(); + } +#endif + return S_OK; +} + +// Unswizzle. +// Format is: + +// 00 01 04 05 +// 02 03 06 07 +// 08 09 12 13 +// 10 11 14 15 ... + +// Currently only works for 32bit and 8bit textures, with power of 2 width and height +void Unswizzle(const void *src, unsigned int depth, unsigned int width, unsigned int height, void *dest) +{ + for (UINT y = 0; y < height; y++) + { + UINT sy = 0; + if (y < width) + { + for (int bit = 0; bit < 16; bit++) + sy |= ((y >> bit) & 1) << (2*bit); + sy <<= 1; // y counts twice + } + else + { + UINT y_mask = y % width; + for (int bit = 0; bit < 16; bit++) + sy |= ((y_mask >> bit) & 1) << (2*bit); + sy <<= 1; // y counts twice + sy += (y / width) * width * width; + } + BYTE *d = (BYTE *)dest + y * width * depth; + for (UINT x = 0; x < width; x++) + { + UINT sx = 0; + if (x < height * 2) + { + for (int bit = 0; bit < 16; bit++) + sx |= ((x >> bit) & 1) << (2*bit); + } + else + { + int x_mask = x % (2*height); + for (int bit = 0; bit < 16; bit++) + sx |= ((x_mask >> bit) & 1) << (2*bit); + sx += (x / (2 * height)) * 2 * height * height; + } + BYTE *s = (BYTE *)src + (sx + sy)*depth; + for (unsigned int i = 0; i < depth; ++i) + *d++ = *s++; + } + } +} + +void DXT1toARGB(const void *src, void *dest, unsigned int destWidth) +{ + const BYTE *b = (const BYTE *)src; + // colour is in R5G6B5 format, convert to R8G8B8 + DWORD colour[4]; + BYTE red[4]; + BYTE green[4]; + BYTE blue[4]; + for (int i = 0; i < 2; i++) + { + red[i] = b[2*i+1] & 0xf8; + green[i] = ((b[2*i+1] & 0x7) << 5) | ((b[2*i] & 0xe0) >> 3); + blue[i] = (b[2*i] & 0x1f) << 3; + colour[i] = (red[i] << 16) | (green[i] << 8) | blue[i]; + } + if (colour[0] > colour[1]) + { + red[2] = (2 * red[0] + red[1] + 1) / 3; + green[2] = (2 * green[0] + green[1] + 1) / 3; + blue[2] = (2 * blue[0] + blue[1] + 1) / 3; + red[3] = (red[0] + 2 * red[1] + 1) / 3; + green[3] = (green[0] + 2 * green[1] + 1) / 3; + blue[3] = (blue[0] + 2 * blue[1] + 1) / 3; + for (int i = 0; i < 4; i++) + colour[i] = (red[i] << 16) | (green[i] << 8) | blue[i] | 0xFF000000; + } + else + { + red[2] = (red[0] + red[1]) / 2; + green[2] = (green[0] + green[1]) / 2; + blue[2] = (blue[0] + blue[1]) / 2; + for (int i = 0; i < 3; i++) + colour[i] = (red[i] << 16) | (green[i] << 8) | blue[i] | 0xFF000000; + colour[3] = 0; // transparent + } + // ok, now grab the bits + for (int y = 0; y < 4; y++) + { + DWORD *d = (DWORD *)dest + destWidth * y; + *d++ = colour[(b[4 + y] & 0x03)]; + *d++ = colour[(b[4 + y] & 0x0c) >> 2]; + *d++ = colour[(b[4 + y] & 0x30) >> 4]; + *d++ = colour[(b[4 + y] & 0xc0) >> 6]; + } +} + +void DXT4toARGB(const void *src, void *dest, unsigned int destWidth) +{ + const BYTE *b = (const BYTE *)src; + BYTE alpha[8]; + alpha[0] = b[0]; + alpha[1] = b[1]; + if (alpha[0] > alpha[1]) + { + alpha[2] = (6 * alpha[0] + 1 * alpha[1]+ 3) / 7; + alpha[3] = (5 * alpha[0] + 2 * alpha[1] + 3) / 7; // bit code 011 + alpha[4] = (4 * alpha[0] + 3 * alpha[1] + 3) / 7; // bit code 100 + alpha[5] = (3 * alpha[0] + 4 * alpha[1] + 3) / 7; // bit code 101 + alpha[6] = (2 * alpha[0] + 5 * alpha[1] + 3) / 7; // bit code 110 + alpha[7] = (1 * alpha[0] + 6 * alpha[1] + 3) / 7; // bit code 111 + } + else + { + alpha[2] = (4 * alpha[0] + 1 * alpha[1] + 2) / 5; // Bit code 010 + alpha[3] = (3 * alpha[0] + 2 * alpha[1] + 2) / 5; // Bit code 011 + alpha[4] = (2 * alpha[0] + 3 * alpha[1] + 2) / 5; // Bit code 100 + alpha[5] = (1 * alpha[0] + 4 * alpha[1] + 2) / 5; // Bit code 101 + alpha[6] = 0; // Bit code 110 + alpha[7] = 255; // Bit code 111 + } + // ok, now grab the bits + BYTE a[4][4]; + a[0][0] = alpha[(b[2] & 0xe0) >> 5]; + a[0][1] = alpha[(b[2] & 0x1c) >> 2]; + a[0][2] = alpha[((b[2] & 0x03) << 1) | ((b[3] & 0x80) >> 7)]; + a[0][3] = alpha[(b[3] & 0x70) >> 4]; + a[1][0] = alpha[(b[3] & 0x0e) >> 1]; + a[1][1] = alpha[((b[3] & 0x01) << 2) | ((b[4] & 0xc0) >> 6)]; + a[1][2] = alpha[(b[4] & 0x38) >> 3]; + a[1][3] = alpha[(b[4] & 0x07)]; + a[2][0] = alpha[(b[5] & 0xe0) >> 5]; + a[2][1] = alpha[(b[5] & 0x1c) >> 2]; + a[2][2] = alpha[((b[5] & 0x03) << 1) | ((b[6] & 0x80) >> 7)]; + a[2][3] = alpha[(b[6] & 0x70) >> 4]; + a[3][0] = alpha[(b[6] & 0x0e) >> 1]; + a[3][1] = alpha[((b[6] & 0x01) << 2) | ((b[7] & 0xc0) >> 6)]; + a[3][2] = alpha[(b[7] & 0x38) >> 3]; + a[3][3] = alpha[(b[7] & 0x07)]; + + b = (BYTE *)src + 8; + // colour is in R5G6B5 format, convert to R8G8B8 + DWORD colour[4]; + BYTE red[4]; + BYTE green[4]; + BYTE blue[4]; + for (int i = 0; i < 2; i++) + { + red[i] = b[2*i+1] & 0xf8; + green[i] = ((b[2*i+1] & 0x7) << 5) | ((b[2*i] & 0xe0) >> 3); + blue[i] = (b[2*i] & 0x1f) << 3; + } + red[2] = (2 * red[0] + red[1] + 1) / 3; + green[2] = (2 * green[0] + green[1] + 1) / 3; + blue[2] = (2 * blue[0] + blue[1] + 1) / 3; + red[3] = (red[0] + 2 * red[1] + 1) / 3; + green[3] = (green[0] + 2 * green[1] + 1) / 3; + blue[3] = (blue[0] + 2 * blue[1] + 1) / 3; + for (int i = 0; i < 4; i++) + colour[i] = (red[i] << 16) | (green[i] << 8) | blue[i]; + // and assign them to our texture + for (int y = 0; y < 4; y++) + { + DWORD *d = (DWORD *)dest + destWidth * y; + *d++ = colour[(b[4 + y] & 0x03)] | (a[y][0] << 24); + *d++ = colour[(b[4 + y] & 0x0e) >> 2] | (a[y][1] << 24); + *d++ = colour[(b[4 + y] & 0x30) >> 4] | (a[y][2] << 24); + *d++ = colour[(b[4 + y] & 0xe0) >> 6] | (a[y][3] << 24); + } + +} + +void ConvertDXT1(const void *src, unsigned int width, unsigned int height, void *dest) +{ + for (unsigned int y = 0; y < height; y += 4) + { + for (unsigned int x = 0; x < width; x += 4) + { + const BYTE *s = (const BYTE *)src + y * width / 2 + x * 2; + DWORD *d = (DWORD *)dest + y * width + x; + DXT1toARGB(s, d, width); + } + } +} + +void ConvertDXT4(const void *src, unsigned int width, unsigned int height, void *dest) +{ + // [4 4 4 4][4 4 4 4] + // + // + // + for (unsigned int y = 0; y < height; y += 4) + { + for (unsigned int x = 0; x < width; x += 4) + { + const BYTE *s = (const BYTE *)src + y * width + x * 4; + DWORD *d = (DWORD *)dest + y * width + x; + DXT4toARGB(s, d, width); + } + } +} + +void GetTextureFromData(D3DTexture *pTex, void *texData, CBaseTexture **ppTexture) +{ + XB_D3DFORMAT fmt; + DWORD width, height, pitch, offset; + ParseTextureHeader(pTex, fmt, width, height, pitch, offset); + + *ppTexture = new CTexture(width, height, 32); + + if (*ppTexture) + { + BYTE *texDataStart = (BYTE *)texData; + COLOR *color = (COLOR *)texData; + texDataStart += offset; +/* DXMERGE - We should really support DXT1,DXT2 and DXT4 in both renderers + Perhaps we should extend CTexture::Update() to support a bunch of different texture types + Rather than assuming linear 32bits + We could just override, as at least then all the loading code from various texture formats + will be in one place + + BYTE *dstPixels = (BYTE *)lr.pBits; + DWORD destPitch = lr.Pitch; + if (fmt == XB_D3DFMT_DXT1) // Not sure if these are 100% correct, but they seem to work :P + { + pitch /= 2; + destPitch /= 4; + } + else if (fmt == XB_D3DFMT_DXT2) + { + destPitch /= 4; + } + else if (fmt == XB_D3DFMT_DXT4) + { + pitch /= 4; + destPitch /= 4; + } +*/ + if (fmt == XB_D3DFMT_DXT1) + { + pitch = width * 4; + BYTE *decoded = new BYTE[pitch * height]; + ConvertDXT1(texDataStart, width, height, decoded); + texDataStart = decoded; + } + else if (fmt == XB_D3DFMT_DXT2 || fmt == XB_D3DFMT_DXT4) + { + pitch = width * 4; + BYTE *decoded = new BYTE[pitch * height]; + ConvertDXT4(texDataStart, width, height, decoded); + texDataStart = decoded; + } + if (IsSwizzledFormat(fmt)) + { // first we unswizzle + BYTE *unswizzled = new BYTE[pitch * height]; + Unswizzle(texDataStart, BytesPerPixelFromFormat(fmt), width, height, unswizzled); + texDataStart = unswizzled; + } + + if (IsPalettedFormat(fmt)) + (*ppTexture)->LoadPaletted(width, height, pitch, texDataStart, color); + else + (*ppTexture)->LoadFromMemory(width, height, pitch, 32, texDataStart); + + if (IsSwizzledFormat(fmt) || fmt == XB_D3DFMT_DXT1 || fmt == XB_D3DFMT_DXT2 || fmt == XB_D3DFMT_DXT4) + { + delete[] texDataStart; + } + } +} diff --git a/guilib/DirectXGraphics.h b/guilib/DirectXGraphics.h new file mode 100644 index 0000000000..45a58365bf --- /dev/null +++ b/guilib/DirectXGraphics.h @@ -0,0 +1,176 @@ +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +class CBaseTexture; + +#include "gui3d.h" + +LPVOID XPhysicalAlloc(SIZE_T s, DWORD ulPhysicalAddress, DWORD ulAlignment, DWORD flProtect); +void XPhysicalFree(LPVOID lpAddress); + +// XPR header +struct XPR_HEADER +{ + DWORD dwMagic; + DWORD dwTotalSize; + DWORD dwHeaderSize; +}; +#define XPR_MAGIC_VALUE (('0' << 24) | ('R' << 16) | ('P' << 8) | 'X') + +typedef enum _XB_D3DFORMAT +{ + XB_D3DFMT_UNKNOWN = 0xFFFFFFFF, + + /* Swizzled formats */ + + XB_D3DFMT_A8R8G8B8 = 0x00000006, + XB_D3DFMT_X8R8G8B8 = 0x00000007, + XB_D3DFMT_R5G6B5 = 0x00000005, + XB_D3DFMT_R6G5B5 = 0x00000027, + XB_D3DFMT_X1R5G5B5 = 0x00000003, + XB_D3DFMT_A1R5G5B5 = 0x00000002, + XB_D3DFMT_A4R4G4B4 = 0x00000004, + XB_D3DFMT_A8 = 0x00000019, + XB_D3DFMT_A8B8G8R8 = 0x0000003A, + XB_D3DFMT_B8G8R8A8 = 0x0000003B, + XB_D3DFMT_R4G4B4A4 = 0x00000039, + XB_D3DFMT_R5G5B5A1 = 0x00000038, + XB_D3DFMT_R8G8B8A8 = 0x0000003C, + XB_D3DFMT_R8B8 = 0x00000029, + XB_D3DFMT_G8B8 = 0x00000028, + + XB_D3DFMT_P8 = 0x0000000B, + + XB_D3DFMT_L8 = 0x00000000, + XB_D3DFMT_A8L8 = 0x0000001A, + XB_D3DFMT_AL8 = 0x00000001, + XB_D3DFMT_L16 = 0x00000032, + + XB_D3DFMT_V8U8 = 0x00000028, + XB_D3DFMT_L6V5U5 = 0x00000027, + XB_D3DFMT_X8L8V8U8 = 0x00000007, + XB_D3DFMT_Q8W8V8U8 = 0x0000003A, + XB_D3DFMT_V16U16 = 0x00000033, + + XB_D3DFMT_D16_LOCKABLE = 0x0000002C, + XB_D3DFMT_D16 = 0x0000002C, + XB_D3DFMT_D24S8 = 0x0000002A, + XB_D3DFMT_F16 = 0x0000002D, + XB_D3DFMT_F24S8 = 0x0000002B, + + /* YUV formats */ + + XB_D3DFMT_YUY2 = 0x00000024, + XB_D3DFMT_UYVY = 0x00000025, + + /* Compressed formats */ + + XB_D3DFMT_DXT1 = 0x0000000C, + XB_D3DFMT_DXT2 = 0x0000000E, + XB_D3DFMT_DXT3 = 0x0000000E, + XB_D3DFMT_DXT4 = 0x0000000F, + XB_D3DFMT_DXT5 = 0x0000000F, + + /* Linear formats */ + + XB_D3DFMT_LIN_A1R5G5B5 = 0x00000010, + XB_D3DFMT_LIN_A4R4G4B4 = 0x0000001D, + XB_D3DFMT_LIN_A8 = 0x0000001F, + XB_D3DFMT_LIN_A8B8G8R8 = 0x0000003F, + XB_D3DFMT_LIN_A8R8G8B8 = 0x00000012, + XB_D3DFMT_LIN_B8G8R8A8 = 0x00000040, + XB_D3DFMT_LIN_G8B8 = 0x00000017, + XB_D3DFMT_LIN_R4G4B4A4 = 0x0000003E, + XB_D3DFMT_LIN_R5G5B5A1 = 0x0000003D, + XB_D3DFMT_LIN_R5G6B5 = 0x00000011, + XB_D3DFMT_LIN_R6G5B5 = 0x00000037, + XB_D3DFMT_LIN_R8B8 = 0x00000016, + XB_D3DFMT_LIN_R8G8B8A8 = 0x00000041, + XB_D3DFMT_LIN_X1R5G5B5 = 0x0000001C, + XB_D3DFMT_LIN_X8R8G8B8 = 0x0000001E, + + XB_D3DFMT_LIN_A8L8 = 0x00000020, + XB_D3DFMT_LIN_AL8 = 0x0000001B, + XB_D3DFMT_LIN_L16 = 0x00000035, + XB_D3DFMT_LIN_L8 = 0x00000013, + + XB_D3DFMT_LIN_V16U16 = 0x00000036, + XB_D3DFMT_LIN_V8U8 = 0x00000017, + XB_D3DFMT_LIN_L6V5U5 = 0x00000037, + XB_D3DFMT_LIN_X8L8V8U8 = 0x0000001E, + XB_D3DFMT_LIN_Q8W8V8U8 = 0x00000012, + + XB_D3DFMT_LIN_D24S8 = 0x0000002E, + XB_D3DFMT_LIN_F24S8 = 0x0000002F, + XB_D3DFMT_LIN_D16 = 0x00000030, + XB_D3DFMT_LIN_F16 = 0x00000031, + + XB_D3DFMT_VERTEXDATA = 100, + XB_D3DFMT_INDEX16 = 101, + + XB_D3DFMT_FORCE_DWORD =0x7fffffff +} XB_D3DFORMAT; + +D3DFORMAT GetD3DFormat(XB_D3DFORMAT format); +DWORD BytesPerPixelFromFormat(XB_D3DFORMAT format); +bool IsPalettedFormat(XB_D3DFORMAT format); +void ParseTextureHeader(D3DTexture *tex, XB_D3DFORMAT &fmt, DWORD &width, DWORD &height, DWORD &pitch, DWORD &offset); +bool IsSwizzledFormat(XB_D3DFORMAT format); + +#ifndef _LINUX +typedef unsigned __int32 uint32_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +#endif + +#pragma pack(push, 2) +typedef struct { + uint8_t id[2]; // offset + uint32_t filesize; // 2 + uint32_t reserved; // 6 + uint32_t headersize; // 10 + uint32_t infoSize; // 14 + uint32_t width; // 18 + uint32_t height; // 22 + uint16_t biPlanes; // 26 + uint16_t bits; // 28 + uint32_t biCompression; // 30 + uint32_t biSizeImage; // 34 + uint32_t biXPelsPerMeter; // 38 + uint32_t biYPelsPerMeter; // 42 + uint32_t biClrUsed; // 46 + uint32_t biClrImportant; // 50 +} BMPHEAD; +#pragma pack(pop) + +#ifdef HAS_DX +HRESULT XGWriteSurfaceToFile(LPDIRECT3DSURFACE9 pSurface, const char *fileName); +#else +HRESULT XGWriteSurfaceToFile(void* pixels, int width, int height, const char *fileName); +#endif +void Unswizzle(const void *src, unsigned int depth, unsigned int width, unsigned int height, void *dest); +void DXT1toARGB(const void *src, void *dest, unsigned int destWidth); +void DXT4toARGB(const void *src, void *dest, unsigned int destWidth); +void ConvertDXT1(const void *src, unsigned int width, unsigned int height, void *dest); +void ConvertDXT4(const void *src, unsigned int width, unsigned int height, void *dest); +void GetTextureFromData(D3DTexture *pTex, void *texData, CBaseTexture** ppTexture); diff --git a/guilib/FrameBufferObject.cpp b/guilib/FrameBufferObject.cpp new file mode 100644 index 0000000000..fb9530c8da --- /dev/null +++ b/guilib/FrameBufferObject.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" + +#ifdef HAS_GL +#include "../xbmc/Settings.h" +#include "WindowingFactory.h" +#include "FrameBufferObject.h" +#include "utils/log.h" + +////////////////////////////////////////////////////////////////////// +// CFrameBufferObject +////////////////////////////////////////////////////////////////////// + +CFrameBufferObject::CFrameBufferObject() +{ + m_fbo = 0; + m_valid = false; + m_supported = false; + m_bound = false; + m_texid = 0; +} + +bool CFrameBufferObject::IsSupported() +{ + if(g_Windowing.IsExtSupported("GL_EXT_framebuffer_object")) + m_supported = true; + else + m_supported = false; + return m_supported; +} + +bool CFrameBufferObject::Initialize() +{ + if (!IsSupported()) + return false; + + Cleanup(); + + glGenFramebuffersEXT(1, &m_fbo); + VerifyGLState(); + + if (!m_fbo) + return false; + + m_valid = true; + return true; +} + +void CFrameBufferObject::Cleanup() +{ + if (!IsValid()) + return; + + if (m_fbo) + glDeleteFramebuffersEXT(1, &m_fbo); + + if (m_texid) + glDeleteTextures(1, &m_texid); + + m_texid = 0; + m_fbo = 0; + m_valid = false; + m_bound = false; +} + +bool CFrameBufferObject::CreateAndBindToTexture(GLenum target, int width, int height, GLenum format, + GLenum filter, GLenum clampmode) +{ + if (!IsValid()) + return false; + + if (m_texid) + glDeleteTextures(1, &m_texid); + + glGenTextures(1, &m_texid); + glBindTexture(target, m_texid); + glTexImage2D(target, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(target, GL_TEXTURE_WRAP_S, clampmode); + glTexParameteri(target, GL_TEXTURE_WRAP_T, clampmode); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter); + VerifyGLState(); + return BindToTexture(target, m_texid); +} + +void CFrameBufferObject::SetFiltering(GLenum target, GLenum mode) +{ + glBindTexture(target, m_texid); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, mode); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, mode); +} + +bool CFrameBufferObject::BindToTexture(GLenum target, GLuint texid) +{ + if (!IsValid()) + return false; + + m_bound = false; + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); + glBindTexture(target, texid); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, target, texid, 0); + VerifyGLState(); + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) + { + VerifyGLState(); + return false; + } + m_bound = true; + return true; +} + +#endif diff --git a/guilib/FrameBufferObject.h b/guilib/FrameBufferObject.h new file mode 100644 index 0000000000..cc3d679daf --- /dev/null +++ b/guilib/FrameBufferObject.h @@ -0,0 +1,111 @@ +#ifndef __FRAMEBUFFEROBJECT_H__ +#define __FRAMEBUFFEROBJECT_H__ + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" + +#ifdef HAS_GL + +// +// CFrameBufferObject +// A class that abstracts FBOs to facilitate Render To Texture +// +// Requires OpenGL 1.5+ or the GL_EXT_framebuffer_object extension. +// +// Usage: +// +// CFrameBufferObject *fbo = new CFrameBufferObject(); +// fbo->Initialize(); +// fbo->CreateAndBindToTexture(GL_TEXTURE_2D, 256, 256, GL_RGBA); +// OR fbo->BindToTexture(GL_TEXTURE_2D, <existing texture ID>); +// fbo->BeginRender(); +// <normal GL rendering calls> +// fbo->EndRender(); +// bind and use texture anywhere +// glBindTexture(GL_TEXTURE_2D, fbo->Texture()); +// + + +class CFrameBufferObject +{ +public: + // Constructor + CFrameBufferObject(); + + // returns true if FBO support is detected + bool IsSupported(); + + // returns true if FBO has been initialized + bool IsValid() { return m_valid; } + + // returns true if FBO has a texture bound to it + bool IsBound() { return m_bound; } + + // initialize the FBO + bool Initialize(); + + // Cleanup + void Cleanup(); + + // Bind to an exiting texture + bool BindToTexture(GLenum target, GLuint texid); + + // Set texture filtering + void SetFiltering(GLenum target, GLenum mode); + + // Create a new texture and bind to it + bool CreateAndBindToTexture(GLenum target, int width, int height, GLenum format, + GLenum filter=GL_LINEAR, GLenum clamp=GL_CLAMP_TO_EDGE); + + // Return the internally created texture ID + GLuint Texture() { return m_texid; } + + // Begin rendering to FBO + bool BeginRender() + { + if (IsValid() && IsBound()) + { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); + return true; + } + return false; + } + + // Finish rendering to FBO + void EndRender() + { + if (IsValid()) + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + } + +private: + GLuint m_fbo; + bool m_valid; + bool m_bound; + bool m_supported; + GLuint m_texid; +}; + +#endif + +#endif diff --git a/guilib/GUIActionDescriptor.h b/guilib/GUIActionDescriptor.h new file mode 100644 index 0000000000..6bd0ec1966 --- /dev/null +++ b/guilib/GUIActionDescriptor.h @@ -0,0 +1,36 @@ +#ifndef GUI_ACTION_DESCRIPTOR +#define GUI_ACTION_DESCRIPTOR + +#include "StdString.h" +#include "system.h" + +class CGUIActionDescriptor +{ +public: + typedef enum { LANG_XBMC = 0, LANG_PYTHON = 1 /*, LANG_JAVASCRIPT = 2 */ } ActionLang; + + CGUIActionDescriptor() + { + m_lang = LANG_XBMC; + m_action = ""; + m_sourceWindowId = -1; + } + + CGUIActionDescriptor(CStdString& action) + { + m_lang = LANG_XBMC; + m_action = action; + } + + CGUIActionDescriptor(ActionLang lang, CStdString& action) + { + m_lang = lang; + m_action = action; + } + + CStdString m_action; + ActionLang m_lang; + DWORD m_sourceWindowId; // the id of the window that was a source of an action +}; + +#endif diff --git a/guilib/GUIAudioManager.cpp b/guilib/GUIAudioManager.cpp new file mode 100644 index 0000000000..8b7dfee7cc --- /dev/null +++ b/guilib/GUIAudioManager.cpp @@ -0,0 +1,445 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" +#include "GUIAudioManager.h" +#include "Key.h" +#include "AudioContext.h" +#include "GUISound.h" +#include "GUISettings.h" +#include "ButtonTranslator.h" +#include "utils/SingleLock.h" +#include "../xbmc/Util.h" +#include "../xbmc/FileSystem/Directory.h" +#include "tinyXML/tinyxml.h" +#ifdef HAS_SDL_AUDIO +#include <SDL/SDL_mixer.h> +#endif + +using namespace std; +using namespace DIRECTORY; + +CGUIAudioManager g_audioManager; + +CGUIAudioManager::CGUIAudioManager() +{ + m_bInitialized = false; + m_actionSound=NULL; + m_bEnabled=true; +} + +CGUIAudioManager::~CGUIAudioManager() +{ + +} + +void CGUIAudioManager::Initialize(int iDevice) +{ + if (m_bInitialized || !m_bEnabled) + return; + + if (g_guiSettings.GetString("lookandfeel.soundskin")=="OFF") + return; + + if (iDevice==CAudioContext::DEFAULT_DEVICE) + { + CSingleLock lock(m_cs); + CLog::Log(LOGDEBUG, "CGUIAudioManager::Initialize"); +#ifdef _WIN32 + bool bAudioOnAllSpeakers=false; + g_audioContext.SetupSpeakerConfig(2, bAudioOnAllSpeakers); + g_audioContext.SetActiveDevice(CAudioContext::DIRECTSOUND_DEVICE); + m_bInitialized = true; +#elif defined(HAS_SDL_AUDIO) + Mix_CloseAudio(); + if (Mix_OpenAudio(44100, AUDIO_S16, 2, 4096)) + CLog::Log(LOGERROR, "Unable to open audio mixer"); + m_bInitialized = true; +#endif + } +} + +void CGUIAudioManager::DeInitialize(int iDevice) +{ + if (!(iDevice == CAudioContext::DIRECTSOUND_DEVICE || iDevice == CAudioContext::DEFAULT_DEVICE)) return; + + if (!m_bInitialized) + return; + + CSingleLock lock(m_cs); + CLog::Log(LOGDEBUG, "CGUIAudioManager::DeInitialize"); + + if (m_actionSound) // Wait for finish when an action sound is playing + while(m_actionSound->IsPlaying()) {} + + Stop(); +#ifdef HAS_SDL_AUDIO + Mix_CloseAudio(); +#endif + m_bInitialized = false; + +} +void CGUIAudioManager::Stop() +{ + CSingleLock lock(m_cs); + if (m_actionSound) + { + delete m_actionSound; + m_actionSound=NULL; + } + + for (windowSoundsMap::iterator it=m_windowSounds.begin();it!=m_windowSounds.end();it++) + { + CGUISound* sound=it->second; + if (sound->IsPlaying()) + sound->Stop(); + + delete sound; + } + m_windowSounds.clear(); + + for (pythonSoundsMap::iterator it1=m_pythonSounds.begin();it1!=m_pythonSounds.end();it1++) + { + CGUISound* sound=it1->second; + if (sound->IsPlaying()) + sound->Stop(); + + delete sound; + } + m_pythonSounds.clear(); +} + +// \brief Clear any unused audio buffers +void CGUIAudioManager::FreeUnused() +{ + CSingleLock lock(m_cs); + + // Free the sound from the last action + if (m_actionSound && !m_actionSound->IsPlaying()) + { + delete m_actionSound; + m_actionSound=NULL; + } + + // Free sounds from windows + windowSoundsMap::iterator it=m_windowSounds.begin(); + while (it!=m_windowSounds.end()) + { + CGUISound* sound=it->second; + if (!sound->IsPlaying()) + { + delete sound; + m_windowSounds.erase(it++); + } + else ++it; + } + + // Free sounds from python + pythonSoundsMap::iterator it1=m_pythonSounds.begin(); + while (it1!=m_pythonSounds.end()) + { + CGUISound* sound=it1->second; + if (!sound->IsPlaying()) + { + delete sound; + m_pythonSounds.erase(it1++); + } + else ++it1; + } +} + +// \brief Play a sound associated with a CAction +void CGUIAudioManager::PlayActionSound(const CAction& action) +{ + // it's not possible to play gui sounds when passthrough is active + if (!m_bInitialized || !m_bEnabled || g_audioContext.IsPassthroughActive()) + return; + + CSingleLock lock(m_cs); + + actionSoundMap::iterator it=m_actionSoundMap.find(action.id); + if (it==m_actionSoundMap.end()) + return; + + if (m_actionSound) + { + delete m_actionSound; + m_actionSound=NULL; + } + + m_actionSound=new CGUISound(); + if (!m_actionSound->Load(CUtil::AddFileToFolder(m_strMediaDir, it->second))) + { + delete m_actionSound; + m_actionSound=NULL; + return; + } + + m_actionSound->Play(); +} + +// \brief Play a sound associated with a window and its event +// Events: SOUND_INIT, SOUND_DEINIT +void CGUIAudioManager::PlayWindowSound(int id, WINDOW_SOUND event) +{ + // it's not possible to play gui sounds when passthrough is active + if (!m_bInitialized || !m_bEnabled || g_audioContext.IsPassthroughActive()) + return; + + CSingleLock lock(m_cs); + + windowSoundMap::iterator it=m_windowSoundMap.find(id); + if (it==m_windowSoundMap.end()) + return; + + CWindowSounds sounds=it->second; + CStdString strFile; + switch (event) + { + case SOUND_INIT: + strFile=sounds.strInitFile; + break; + case SOUND_DEINIT: + strFile=sounds.strDeInitFile; + break; + } + + if (strFile.IsEmpty()) + return; + + // One sound buffer for each window + windowSoundsMap::iterator itsb=m_windowSounds.find(id); + if (itsb!=m_windowSounds.end()) + { + CGUISound* sound=itsb->second; + if (sound->IsPlaying()) + sound->Stop(); + delete sound; + m_windowSounds.erase(itsb++); + } + + CGUISound* sound=new CGUISound(); + if (!sound->Load(CUtil::AddFileToFolder(m_strMediaDir, strFile))) + { + delete sound; + return; + } + + m_windowSounds.insert(pair<int, CGUISound*>(id, sound)); + sound->Play(); +} + +// \brief Play a sound given by filename +void CGUIAudioManager::PlayPythonSound(const CStdString& strFileName) +{ + // it's not possible to play gui sounds when passthrough is active + if (!m_bInitialized || !m_bEnabled || g_audioContext.IsPassthroughActive()) + return; + + CSingleLock lock(m_cs); + + // If we already loaded the sound, just play it + pythonSoundsMap::iterator itsb=m_pythonSounds.find(strFileName); + if (itsb!=m_pythonSounds.end()) + { + CGUISound* sound=itsb->second; + if (sound->IsPlaying()) + sound->Stop(); + + sound->Play(); + + return; + } + + CGUISound* sound=new CGUISound(); + if (!sound->Load(strFileName)) + { + delete sound; + return; + } + + m_pythonSounds.insert(pair<CStdString, CGUISound*>(strFileName, sound)); + sound->Play(); +} + +// \brief Load the config file (sounds.xml) for nav sounds +// Can be located in a folder "sounds" in the skin or from a +// subfolder of the folder "sounds" in the root directory of +// xbmc +bool CGUIAudioManager::Load() +{ + m_actionSoundMap.clear(); + m_windowSoundMap.clear(); + + Enable(m_bEnabled); // make sure we initialize or deinitialize + + if (g_guiSettings.GetString("lookandfeel.soundskin")=="OFF") + return true; + + if (g_guiSettings.GetString("lookandfeel.soundskin")=="SKINDEFAULT") + { + m_strMediaDir="special://home/skin/" + g_guiSettings.GetString("lookandfeel.skin") + "/sounds"; + if ( ! CDirectory::Exists( m_strMediaDir ) ) + { + m_strMediaDir = CUtil::AddFileToFolder("special://xbmc/skin", g_guiSettings.GetString("lookandfeel.skin")); + m_strMediaDir = CUtil::AddFileToFolder(m_strMediaDir, "sounds"); + } + } + else + m_strMediaDir = CUtil::AddFileToFolder("special://xbmc/sounds", g_guiSettings.GetString("lookandfeel.soundskin")); + + CStdString strSoundsXml = CUtil::AddFileToFolder(m_strMediaDir, "sounds.xml"); + + // Load our xml file + TiXmlDocument xmlDoc; + + CLog::Log(LOGINFO, "Loading %s", strSoundsXml.c_str()); + + // Load the config file + if (!xmlDoc.LoadFile(strSoundsXml)) + { + CLog::Log(LOGNOTICE, "%s, Line %d\n%s", strSoundsXml.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc()); + return false; + } + + TiXmlElement* pRoot = xmlDoc.RootElement(); + CStdString strValue = pRoot->Value(); + if ( strValue != "sounds") + { + CLog::Log(LOGNOTICE, "%s Doesn't contain <sounds>", strSoundsXml.c_str()); + return false; + } + + // Load sounds for actions + TiXmlElement* pActions = pRoot->FirstChildElement("actions"); + if (pActions) + { + TiXmlNode* pAction = pActions->FirstChild("action"); + + while (pAction) + { + TiXmlNode* pIdNode = pAction->FirstChild("name"); + int id = 0; // action identity + if (pIdNode && pIdNode->FirstChild()) + { + CButtonTranslator::TranslateActionString(pIdNode->FirstChild()->Value(), id); + } + + TiXmlNode* pFileNode = pAction->FirstChild("file"); + CStdString strFile; + if (pFileNode && pFileNode->FirstChild()) + strFile+=pFileNode->FirstChild()->Value(); + + if (id > 0 && !strFile.IsEmpty()) + m_actionSoundMap.insert(pair<int, CStdString>(id, strFile)); + + pAction = pAction->NextSibling(); + } + } + + // Load window specific sounds + TiXmlElement* pWindows = pRoot->FirstChildElement("windows"); + if (pWindows) + { + TiXmlNode* pWindow = pWindows->FirstChild("window"); + + while (pWindow) + { + int id = 0; + + TiXmlNode* pIdNode = pWindow->FirstChild("name"); + if (pIdNode) + { + if (pIdNode->FirstChild()) + id = CButtonTranslator::TranslateWindowString(pIdNode->FirstChild()->Value()); + } + + CWindowSounds sounds; + LoadWindowSound(pWindow, "activate", sounds.strInitFile); + LoadWindowSound(pWindow, "deactivate", sounds.strDeInitFile); + + if (id > 0) + m_windowSoundMap.insert(pair<int, CWindowSounds>(id, sounds)); + + pWindow = pWindow->NextSibling(); + } + } + + return true; +} + +// \brief Load a window node of the config file (sounds.xml) +bool CGUIAudioManager::LoadWindowSound(TiXmlNode* pWindowNode, const CStdString& strIdentifier, CStdString& strFile) +{ + if (!pWindowNode) + return false; + + TiXmlNode* pFileNode = pWindowNode->FirstChild(strIdentifier); + if (pFileNode && pFileNode->FirstChild()) + { + strFile = pFileNode->FirstChild()->Value(); + return true; + } + + return false; +} + +// \brief Enable/Disable nav sounds +void CGUIAudioManager::Enable(bool bEnable) +{ + m_bEnabled=bEnable; + + // always deinit audio when we don't want gui sounds + if (g_guiSettings.GetString("lookandfeel.soundskin")=="OFF") + bEnable = false; + + if (bEnable) + Initialize(CAudioContext::DEFAULT_DEVICE); + else if (!bEnable) + DeInitialize(CAudioContext::DEFAULT_DEVICE); +} + +// \brief Sets the volume of all playing sounds +void CGUIAudioManager::SetVolume(int iLevel) +{ + CSingleLock lock(m_cs); + + if (m_actionSound) + m_actionSound->SetVolume(iLevel); + + windowSoundsMap::iterator it=m_windowSounds.begin(); + while (it!=m_windowSounds.end()) + { + if (it->second) + it->second->SetVolume(iLevel); + + ++it; + } + + pythonSoundsMap::iterator it1=m_pythonSounds.begin(); + while (it1!=m_pythonSounds.end()) + { + if (it1->second) + it1->second->SetVolume(iLevel); + + ++it1; + } +} diff --git a/guilib/GUIAudioManager.h b/guilib/GUIAudioManager.h new file mode 100644 index 0000000000..a31573c913 --- /dev/null +++ b/guilib/GUIAudioManager.h @@ -0,0 +1,88 @@ +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "IAudioDeviceChangedCallback.h" +#include "utils/CriticalSection.h" +#include "utils/log.h" +#include "StdString.h" + +#include <map> + +// forward definitions +class CAction; +class CGUISound; +class TiXmlNode; + +enum WINDOW_SOUND { SOUND_INIT = 0, SOUND_DEINIT }; + +class CGUIAudioManager : public IAudioDeviceChangedCallback +{ + class CWindowSounds + { + public: + CStdString strInitFile; + CStdString strDeInitFile; + }; + +public: + CGUIAudioManager(); + virtual ~CGUIAudioManager(); + + virtual void Initialize(int iDevice); + virtual void DeInitialize(int iDevice); + + bool Load(); + + void PlayActionSound(const CAction& action); + void PlayWindowSound(int id, WINDOW_SOUND event); + void PlayPythonSound(const CStdString& strFileName); + + void FreeUnused(); + + void Enable(bool bEnable); + void SetVolume(int iLevel); + void Stop(); +private: + bool LoadWindowSound(TiXmlNode* pWindowNode, const CStdString& strIdentifier, CStdString& strFile); + + typedef std::map<int, CStdString> actionSoundMap; + typedef std::map<int, CWindowSounds> windowSoundMap; + + typedef std::map<CStdString, CGUISound*> pythonSoundsMap; + typedef std::map<int, CGUISound*> windowSoundsMap; + + actionSoundMap m_actionSoundMap; + windowSoundMap m_windowSoundMap; + + CGUISound* m_actionSound; + windowSoundsMap m_windowSounds; + pythonSoundsMap m_pythonSounds; + + CStdString m_strMediaDir; + bool m_bEnabled; + bool m_bInitialized; + + CCriticalSection m_cs; +}; + +extern CGUIAudioManager g_audioManager; diff --git a/guilib/GUIBaseContainer.cpp b/guilib/GUIBaseContainer.cpp new file mode 100644 index 0000000000..332c6dce34 --- /dev/null +++ b/guilib/GUIBaseContainer.cpp @@ -0,0 +1,1183 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIBaseContainer.h" +#include "GUIControlFactory.h" +#include "utils/CharsetConverter.h" +#include "utils/GUIInfoManager.h" +#include "utils/log.h" +#include "GUILabelControl.h" +#include "XMLUtils.h" +#include "SkinInfo.h" +#include "StringUtils.h" +#include "FileItem.h" +#include "Key.h" + +using namespace std; + +#define HOLD_TIME_START 1000 +#define HOLD_TIME_END 4000 + +CGUIBaseContainer::CGUIBaseContainer(int parentID, int controlID, float posX, float posY, float width, float height, ORIENTATION orientation, int scrollTime, int preloadItems) + : CGUIControl(parentID, controlID, posX, posY, width, height) +{ + m_cursor = 0; + m_offset = 0; + m_scrollOffset = 0; + m_scrollSpeed = 0; + m_scrollLastTime = 0; + m_scrollTime = scrollTime ? scrollTime : 1; + m_lastHoldTime = 0; + m_itemsPerPage = 10; + m_pageControl = 0; + m_renderTime = 0; + m_orientation = orientation; + m_analogScrollCount = 0; + m_lastItem = NULL; + m_staticContent = false; + m_staticUpdateTime = 0; + m_wasReset = false; + m_layout = NULL; + m_focusedLayout = NULL; + m_cacheItems = preloadItems; +} + +CGUIBaseContainer::~CGUIBaseContainer(void) +{ +} + +void CGUIBaseContainer::Render() +{ + ValidateOffset(); + + if (m_bInvalidated) + UpdateLayout(); + + if (!m_layout || !m_focusedLayout) return; + + UpdateScrollOffset(); + + int offset = (int)floorf(m_scrollOffset / m_layout->Size(m_orientation)); + + int cacheBefore, cacheAfter; + GetCacheOffsets(cacheBefore, cacheAfter); + + // Free memory not used on screen + if ((int)m_items.size() > m_itemsPerPage + cacheBefore + cacheAfter) + FreeMemory(CorrectOffset(offset - cacheBefore, 0), CorrectOffset(offset + m_itemsPerPage + 1 + cacheAfter, 0)); + + g_graphicsContext.SetClipRegion(m_posX, m_posY, m_width, m_height); + float pos = (m_orientation == VERTICAL) ? m_posY : m_posX; + float end = (m_orientation == VERTICAL) ? m_posY + m_height : m_posX + m_width; + + // we offset our draw position to take into account scrolling and whether or not our focused + // item is offscreen "above" the list. + float drawOffset = (offset - cacheBefore) * m_layout->Size(m_orientation) - m_scrollOffset; + if (m_offset + m_cursor < offset) + drawOffset += m_focusedLayout->Size(m_orientation) - m_layout->Size(m_orientation); + pos += drawOffset; + end += cacheAfter * m_layout->Size(m_orientation); + + float focusedPos = 0; + CGUIListItemPtr focusedItem; + int current = offset - cacheBefore; + while (pos < end && m_items.size()) + { + int itemNo = CorrectOffset(current, 0); + if (itemNo >= (int)m_items.size()) + break; + bool focused = (current == m_offset + m_cursor); + if (itemNo >= 0) + { + CGUIListItemPtr item = m_items[itemNo]; + // render our item + if (focused) + { + focusedPos = pos; + focusedItem = item; + } + else + { + if (m_orientation == VERTICAL) + RenderItem(m_posX, pos, item.get(), false); + else + RenderItem(pos, m_posY, item.get(), false); + } + } + // increment our position + pos += focused ? m_focusedLayout->Size(m_orientation) : m_layout->Size(m_orientation); + current++; + } + // render focused item last so it can overlap other items + if (focusedItem) + { + if (m_orientation == VERTICAL) + RenderItem(m_posX, focusedPos, focusedItem.get(), true); + else + RenderItem(focusedPos, m_posY, focusedItem.get(), true); + } + + g_graphicsContext.RestoreClipRegion(); + + UpdatePageControl(offset); + + CGUIControl::Render(); +} + + +void CGUIBaseContainer::RenderItem(float posX, float posY, CGUIListItem *item, bool focused) +{ + if (!m_focusedLayout || !m_layout) return; + + // set the origin + g_graphicsContext.SetOrigin(posX, posY); + + if (m_bInvalidated) + item->SetInvalid(); + if (focused) + { + if (!item->GetFocusedLayout()) + { + CGUIListItemLayout *layout = new CGUIListItemLayout(*m_focusedLayout); + item->SetFocusedLayout(layout); + } + if (item->GetFocusedLayout()) + { + if (item != m_lastItem || !HasFocus()) + { + item->GetFocusedLayout()->SetFocusedItem(0); + } + if (item != m_lastItem && HasFocus()) + { + item->GetFocusedLayout()->ResetAnimation(ANIM_TYPE_UNFOCUS); + unsigned int subItem = 1; + if (m_lastItem && m_lastItem->GetFocusedLayout()) + subItem = m_lastItem->GetFocusedLayout()->GetFocusedItem(); + item->GetFocusedLayout()->SetFocusedItem(subItem ? subItem : 1); + } + item->GetFocusedLayout()->Render(item, m_parentID, m_renderTime); + } + m_lastItem = item; + } + else + { + if (item->GetFocusedLayout()) + item->GetFocusedLayout()->SetFocusedItem(0); // focus is not set + if (!item->GetLayout()) + { + CGUIListItemLayout *layout = new CGUIListItemLayout(*m_layout); + item->SetLayout(layout); + } + if (item->GetFocusedLayout() && item->GetFocusedLayout()->IsAnimating(ANIM_TYPE_UNFOCUS)) + item->GetFocusedLayout()->Render(item, m_parentID, m_renderTime); + else if (item->GetLayout()) + item->GetLayout()->Render(item, m_parentID, m_renderTime); + } + g_graphicsContext.RestoreOrigin(); +} + +bool CGUIBaseContainer::OnAction(const CAction &action) +{ + if (action.id >= KEY_ASCII) + { + OnJumpLetter((char)(action.id & 0xff)); + return true; + } + + switch (action.id) + { + case ACTION_MOVE_LEFT: + case ACTION_MOVE_RIGHT: + case ACTION_MOVE_DOWN: + case ACTION_MOVE_UP: + { + if (!HasFocus()) return false; + if (action.holdTime > HOLD_TIME_START && + ((m_orientation == VERTICAL && (action.id == ACTION_MOVE_UP || action.id == ACTION_MOVE_DOWN)) || + (m_orientation == HORIZONTAL && (action.id == ACTION_MOVE_LEFT || action.id == ACTION_MOVE_RIGHT)))) + { // action is held down - repeat a number of times + float speed = std::min(1.0f, (float)(action.holdTime - HOLD_TIME_START) / (HOLD_TIME_END - HOLD_TIME_START)); + unsigned int itemsPerFrame = 1; + if (m_lastHoldTime) // number of rows/10 items/second max speed + itemsPerFrame = std::max((unsigned int)1, (unsigned int)(speed * 0.0001f * GetRows() * (timeGetTime() - m_lastHoldTime))); + m_lastHoldTime = timeGetTime(); + if (action.id == ACTION_MOVE_LEFT || action.id == ACTION_MOVE_UP) + while (itemsPerFrame--) MoveUp(false); + else + while (itemsPerFrame--) MoveDown(false); + return true; + } + else + { + m_lastHoldTime = 0; + return CGUIControl::OnAction(action); + } + } + break; + + case ACTION_FIRST_PAGE: + SelectItem(0); + return true; + + case ACTION_LAST_PAGE: + if (m_items.size()) + SelectItem(m_items.size() - 1); + return true; + + case ACTION_NEXT_LETTER: + { + OnNextLetter(); + return true; + } + break; + case ACTION_PREV_LETTER: + { + OnPrevLetter(); + return true; + } + break; + case ACTION_JUMP_SMS2: + case ACTION_JUMP_SMS3: + case ACTION_JUMP_SMS4: + case ACTION_JUMP_SMS5: + case ACTION_JUMP_SMS6: + case ACTION_JUMP_SMS7: + case ACTION_JUMP_SMS8: + case ACTION_JUMP_SMS9: + { + OnJumpSMS(action.id - ACTION_JUMP_SMS2 + 2); + return true; + } + break; + + default: + if (action.id) + { + return OnClick(action.id); + } + } + return false; +} + +bool CGUIBaseContainer::OnMessage(CGUIMessage& message) +{ + if (message.GetControlId() == GetID() ) + { + if (!m_staticContent) + { + if (message.GetMessage() == GUI_MSG_LABEL_BIND && message.GetPointer()) + { // bind our items + Reset(); + CFileItemList *items = (CFileItemList *)message.GetPointer(); + for (int i = 0; i < items->Size(); i++) + m_items.push_back(items->Get(i)); + UpdateLayout(true); // true to refresh all items + UpdateScrollByLetter(); + SelectItem(message.GetParam1()); + return true; + } + if (message.GetMessage() == GUI_MSG_LABEL_ADD && message.GetItem()) + { + CGUIListItemPtr item = message.GetItem(); + m_items.push_back(item); + UpdateScrollByLetter(); + SetPageControlRange(); + return true; + } + else if (message.GetMessage() == GUI_MSG_LABEL_RESET) + { + Reset(); + SetPageControlRange(); + return true; + } + } + if (message.GetMessage() == GUI_MSG_ITEM_SELECTED) + { + message.SetParam1(GetSelectedItem()); + return true; + } + else if (message.GetMessage() == GUI_MSG_PAGE_CHANGE) + { + if (message.GetSenderId() == m_pageControl && IsVisible()) + { // update our page if we're visible - not much point otherwise + if ((int)message.GetParam1() != m_offset) + m_pageChangeTimer.StartZero(); + ScrollToOffset(message.GetParam1()); + return true; + } + } + else if (message.GetMessage() == GUI_MSG_REFRESH_LIST) + { // update our list contents + for (unsigned int i = 0; i < m_items.size(); ++i) + m_items[i]->SetInvalid(); + } + else if (message.GetMessage() == GUI_MSG_MOVE_OFFSET) + { + int count = (int)message.GetParam1(); + while (count < 0) + { + MoveUp(true); + count++; + } + while (count > 0) + { + MoveDown(true); + count--; + } + return true; + } + } + return CGUIControl::OnMessage(message); +} + +void CGUIBaseContainer::OnUp() +{ + bool wrapAround = m_controlUp == GetID() || !(m_controlUp || m_upActions.size()); + if (m_orientation == VERTICAL && MoveUp(wrapAround)) + return; + // with horizontal lists it doesn't make much sense to have multiselect labels + CGUIControl::OnUp(); +} + +void CGUIBaseContainer::OnDown() +{ + bool wrapAround = m_controlDown == GetID() || !(m_controlDown || m_downActions.size()); + if (m_orientation == VERTICAL && MoveDown(wrapAround)) + return; + // with horizontal lists it doesn't make much sense to have multiselect labels + CGUIControl::OnDown(); +} + +void CGUIBaseContainer::OnLeft() +{ + bool wrapAround = m_controlLeft == GetID() || !(m_controlLeft || m_leftActions.size()); + if (m_orientation == HORIZONTAL && MoveUp(wrapAround)) + return; + else if (m_orientation == VERTICAL) + { + CGUIListItemLayout *focusedLayout = GetFocusedLayout(); + if (focusedLayout && focusedLayout->MoveLeft()) + return; + } + CGUIControl::OnLeft(); +} + +void CGUIBaseContainer::OnRight() +{ + bool wrapAround = m_controlRight == GetID() || !(m_controlRight || m_rightActions.size()); + if (m_orientation == HORIZONTAL && MoveDown(wrapAround)) + return; + else if (m_orientation == VERTICAL) + { + CGUIListItemLayout *focusedLayout = GetFocusedLayout(); + if (focusedLayout && focusedLayout->MoveRight()) + return; + } + CGUIControl::OnRight(); +} + +void CGUIBaseContainer::OnNextLetter() +{ + int offset = CorrectOffset(m_offset, m_cursor); + for (unsigned int i = 0; i < m_letterOffsets.size(); i++) + { + if (m_letterOffsets[i].first > offset) + { + SelectItem(m_letterOffsets[i].first); + return; + } + } +} + +void CGUIBaseContainer::OnPrevLetter() +{ + int offset = CorrectOffset(m_offset, m_cursor); + if (!m_letterOffsets.size()) + return; + for (int i = (int)m_letterOffsets.size() - 1; i >= 0; i--) + { + if (m_letterOffsets[i].first < offset) + { + SelectItem(m_letterOffsets[i].first); + return; + } + } +} + +void CGUIBaseContainer::OnJumpLetter(char letter) +{ + if (m_matchTimer.GetElapsedMilliseconds() < letter_match_timeout) + m_match.push_back(letter); + else + m_match.Format("%c", letter); + + m_matchTimer.StartZero(); + + // we can't jump through letters if we have none + if (0 == m_letterOffsets.size()) + return; + + // find the current letter we're focused on + unsigned int offset = CorrectOffset(m_offset, m_cursor); + for (unsigned int i = (offset + 1) % m_items.size(); i != offset; i = (i+1) % m_items.size()) + { + CGUIListItemPtr item = m_items[i]; + if (0 == strnicmp(item->GetSortLabel().c_str(), m_match.c_str(), m_match.size())) + { + SelectItem(i); + return; + } + } + // no match found - repeat with a single letter + if (m_match.size() > 1) + { + m_match.clear(); + OnJumpLetter(letter); + } +} + +void CGUIBaseContainer::OnJumpSMS(int letter) +{ + static const char letterMap[8][6] = { "ABC2", "DEF3", "GHI4", "JKL5", "MNO6", "PQRS7", "TUV8", "WXYZ9" }; + + // only 2..9 supported + if (letter < 2 || letter > 9 || !m_letterOffsets.size()) + return; + + const CStdString letters = letterMap[letter - 2]; + // find where we currently are + int offset = CorrectOffset(m_offset, m_cursor); + unsigned int currentLetter = 0; + while (currentLetter + 1 < m_letterOffsets.size() && m_letterOffsets[currentLetter + 1].first <= offset) + currentLetter++; + + // now switch to the next letter + CStdString current = m_letterOffsets[currentLetter].second; + int startPos = (letters.Find(current) + 1) % letters.size(); + // now jump to letters[startPos], or another one in the same range if possible + int pos = startPos; + while (true) + { + // check if we can jump to this letter + for (unsigned int i = 0; i < m_letterOffsets.size(); i++) + { + if (m_letterOffsets[i].second == letters.Mid(pos, 1)) + { + SelectItem(m_letterOffsets[i].first); + return; + } + } + pos = (pos + 1) % letters.size(); + if (pos == startPos) + return; + } +} + +bool CGUIBaseContainer::MoveUp(bool wrapAround) +{ + return true; +} + +bool CGUIBaseContainer::MoveDown(bool wrapAround) +{ + return true; +} + +// scrolls the said amount +void CGUIBaseContainer::Scroll(int amount) +{ + ScrollToOffset(m_offset + amount); +} + +int CGUIBaseContainer::GetSelectedItem() const +{ + return CorrectOffset(m_offset, m_cursor); +} + +CGUIListItemPtr CGUIBaseContainer::GetListItem(int offset, unsigned int flag) const +{ + if (!m_items.size()) + return CGUIListItemPtr(); + int item = GetSelectedItem() + offset; + if (flag & INFOFLAG_LISTITEM_POSITION) // use offset from the first item displayed, taking into account scrolling + item = CorrectOffset((int)(m_scrollOffset / m_layout->Size(m_orientation)), offset); + + if (flag & INFOFLAG_LISTITEM_WRAP) + { + item %= ((int)m_items.size()); + if (item < 0) item += m_items.size(); + return m_items[item]; + } + else + { + if (item >= 0 && item < (int)m_items.size()) + return m_items[item]; + } + return CGUIListItemPtr(); +} + +CGUIListItemLayout *CGUIBaseContainer::GetFocusedLayout() const +{ + CGUIListItemPtr item = GetListItem(0); + if (item.get()) return item->GetFocusedLayout(); + return NULL; +} + +bool CGUIBaseContainer::SelectItemFromPoint(const CPoint &point) +{ + if (!m_focusedLayout || !m_layout) + return false; + + int row = 0; + float pos = (m_orientation == VERTICAL) ? point.y : point.x; + while (row < m_itemsPerPage + 1) // 1 more to ensure we get the (possible) half item at the end. + { + const CGUIListItemLayout *layout = (row == m_cursor) ? m_focusedLayout : m_layout; + if (pos < layout->Size(m_orientation) && row + m_offset < (int)m_items.size()) + { // found correct "row" -> check horizontal + if (!InsideLayout(layout, point)) + return false; + + MoveToItem(row); + CGUIListItemLayout *focusedLayout = GetFocusedLayout(); + if (focusedLayout) + { + CPoint pt(point); + if (m_orientation == VERTICAL) + pt.y = pos; + else + pt.x = pos; + focusedLayout->SelectItemFromPoint(pt); + } + return true; + } + row++; + pos -= layout->Size(m_orientation); + } + return false; +} + +bool CGUIBaseContainer::OnMouseOver(const CPoint &point) +{ + // select the item under the pointer + SelectItemFromPoint(point - CPoint(m_posX, m_posY)); + return CGUIControl::OnMouseOver(point); +} + +bool CGUIBaseContainer::OnMouseClick(int button, const CPoint &point) +{ + if (SelectItemFromPoint(point - CPoint(m_posX, m_posY))) + { // send click message to window + OnClick(ACTION_MOUSE_CLICK + button); + return true; + } + return false; +} + +bool CGUIBaseContainer::OnMouseDoubleClick(int button, const CPoint &point) +{ + if (SelectItemFromPoint(point - CPoint(m_posX, m_posY))) + { // send double click message to window + OnClick(ACTION_MOUSE_DOUBLE_CLICK + button); + return true; + } + return false; +} + +bool CGUIBaseContainer::OnClick(int actionID) +{ + int subItem = 0; + if (actionID == ACTION_SELECT_ITEM || actionID == ACTION_MOUSE_LEFT_CLICK) + { + if (m_staticContent) + { // "select" action + int selected = GetSelectedItem(); + if (selected >= 0 && selected < (int)m_items.size()) + { + CFileItemPtr item = boost::static_pointer_cast<CFileItem>(m_items[selected]); + // multiple action strings are concat'd together, separated with " , " + vector<CStdString> actions; + StringUtils::SplitString(item->m_strPath, " , ", actions); + for (unsigned int i = 0; i < actions.size(); i++) + { + CStdString action = actions[i]; + action.Replace(",,", ","); + CGUIMessage message(GUI_MSG_EXECUTE, GetID(), GetParentID()); + message.SetStringParam(action); + g_graphicsContext.SendMessage(message); + } + } + return true; + } + // grab the currently focused subitem (if applicable) + CGUIListItemLayout *focusedLayout = GetFocusedLayout(); + if (focusedLayout) + subItem = focusedLayout->GetFocusedItem(); + } + // Don't know what to do, so send to our parent window. + CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID(), actionID, subItem); + return SendWindowMessage(msg); +} + +bool CGUIBaseContainer::OnMouseWheel(char wheel, const CPoint &point) +{ + Scroll(-wheel); + return true; +} + +CStdString CGUIBaseContainer::GetDescription() const +{ + CStdString strLabel; + int item = GetSelectedItem(); + if (item >= 0 && item < (int)m_items.size()) + { + CGUIListItemPtr pItem = m_items[item]; + if (pItem->m_bIsFolder) + strLabel.Format("[%s]", pItem->GetLabel().c_str()); + else + strLabel = pItem->GetLabel(); + } + return strLabel; +} + +void CGUIBaseContainer::SetFocus(bool bOnOff) +{ + if (bOnOff != HasFocus()) + { + SetInvalid(); + m_lastItem = NULL; + } + CGUIControl::SetFocus(bOnOff); +} + +void CGUIBaseContainer::SaveStates(vector<CControlState> &states) +{ + states.push_back(CControlState(GetID(), GetSelectedItem())); +} + +void CGUIBaseContainer::SetPageControl(int id) +{ + m_pageControl = id; +} + +void CGUIBaseContainer::ValidateOffset() +{ +} + +void CGUIBaseContainer::DoRender(DWORD currentTime) +{ + m_renderTime = currentTime; + CGUIControl::DoRender(currentTime); + if (m_pageChangeTimer.GetElapsedMilliseconds() > 200) + m_pageChangeTimer.Stop(); + m_wasReset = false; +} + +void CGUIBaseContainer::AllocResources() +{ + CalculateLayout(); +} + +void CGUIBaseContainer::FreeResources() +{ + CGUIControl::FreeResources(); + if (m_staticContent) + { // free any static content + Reset(); + m_staticItems.clear(); + } + m_scrollSpeed = 0; +} + +void CGUIBaseContainer::UpdateLayout(bool updateAllItems) +{ + if (updateAllItems) + { // free memory of items + for (iItems it = m_items.begin(); it != m_items.end(); it++) + (*it)->FreeMemory(); + } + // and recalculate the layout + CalculateLayout(); + SetPageControlRange(); +} + +void CGUIBaseContainer::SetPageControlRange() +{ + if (m_pageControl) + { + CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), m_pageControl, m_itemsPerPage, GetRows()); + SendWindowMessage(msg); + } +} + +void CGUIBaseContainer::UpdatePageControl(int offset) +{ + if (m_pageControl) + { // tell our pagecontrol (scrollbar or whatever) to update (offset it by our cursor position) + CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(), m_pageControl, offset); + SendWindowMessage(msg); + } +} + +void CGUIBaseContainer::UpdateVisibility(const CGUIListItem *item) +{ + CGUIControl::UpdateVisibility(item); + + if (!IsVisible()) + return; // no need to update the content if we're not visible + + // check whether we need to update our layouts + if ((m_layout && m_layout->GetCondition() && !g_infoManager.GetBool(m_layout->GetCondition(), GetParentID())) || + (m_focusedLayout && m_focusedLayout->GetCondition() && !g_infoManager.GetBool(m_focusedLayout->GetCondition(), GetParentID()))) + { + // and do it + int item = GetSelectedItem(); + UpdateLayout(true); // true to refresh all items + SelectItem(item); + } + + if (m_staticContent) + { // update our item list with our new content, but only add those items that should + // be visible. Save the previous item and keep it if we are adding that one. + CGUIListItem *lastItem = m_lastItem; + Reset(); + bool updateItems = false; + if (!m_staticUpdateTime) + m_staticUpdateTime = timeGetTime(); + if (timeGetTime() - m_staticUpdateTime > 1000) + { + m_staticUpdateTime = timeGetTime(); + updateItems = true; + } + for (unsigned int i = 0; i < m_staticItems.size(); ++i) + { + CFileItemPtr item = boost::static_pointer_cast<CFileItem>(m_staticItems[i]); + // m_idepth is used to store the visibility condition + if (!item->m_idepth || g_infoManager.GetBool(item->m_idepth, GetParentID())) + { + m_items.push_back(item); + if (item.get() == lastItem) + m_lastItem = lastItem; + } + if (updateItems && item->HasProperties()) + { // has info, so update it + CStdString info = item->GetProperty("label"); + if (!info.IsEmpty()) item->SetLabel(CGUIInfoLabel::GetLabel(info)); + info = item->GetProperty("label2"); + if (!info.IsEmpty()) item->SetLabel2(CGUIInfoLabel::GetLabel(info)); + info = item->GetProperty("icon"); + if (!info.IsEmpty()) item->SetIconImage(CGUIInfoLabel::GetLabel(info, true)); + info = item->GetProperty("thumb"); + if (!info.IsEmpty()) item->SetThumbnailImage(CGUIInfoLabel::GetLabel(info, true)); + } + } + UpdateScrollByLetter(); + } +} + +void CGUIBaseContainer::CalculateLayout() +{ + CGUIListItemLayout *oldFocusedLayout = m_focusedLayout; + CGUIListItemLayout *oldLayout = m_layout; + GetCurrentLayouts(); + + // calculate the number of items to display + assert(m_focusedLayout && m_layout); + if (!m_focusedLayout || !m_layout) return; + + if (oldLayout == m_layout && oldFocusedLayout == m_focusedLayout) + return; // nothing has changed, so don't update stuff + + m_itemsPerPage = (int)((Size() - m_focusedLayout->Size(m_orientation)) / m_layout->Size(m_orientation)) + 1; + + // ensure that the scroll offset is a multiple of our size + m_scrollOffset = m_offset * m_layout->Size(m_orientation); +} + +void CGUIBaseContainer::UpdateScrollByLetter() +{ + m_letterOffsets.clear(); + + // for scrolling by letter we have an offset table into our vector. + CStdString currentMatch; + for (unsigned int i = 0; i < m_items.size(); i++) + { + CGUIListItemPtr item = m_items[i]; + // The letter offset jumping is only for ASCII characters at present, and + // our checks are all done in uppercase + CStdString nextLetter = item->GetSortLabel().Left(1); + nextLetter.ToUpper(); + if (currentMatch != nextLetter) + { + currentMatch = nextLetter; + m_letterOffsets.push_back(make_pair((int)i, currentMatch)); + } + } +} + +unsigned int CGUIBaseContainer::GetRows() const +{ + return m_items.size(); +} + +inline float CGUIBaseContainer::Size() const +{ + return (m_orientation == HORIZONTAL) ? m_width : m_height; +} + +#define MAX_SCROLL_AMOUNT 0.4f + +void CGUIBaseContainer::ScrollToOffset(int offset) +{ + float size = m_layout->Size(m_orientation); + int range = m_itemsPerPage / 4; + if (range <= 0) range = 1; + if (offset * size < m_scrollOffset && m_scrollOffset - offset * size > size * range) + { // scrolling up, and we're jumping more than 0.5 of a screen + m_scrollOffset = (offset + range) * size; + } + if (offset * size > m_scrollOffset && offset * size - m_scrollOffset > size * range) + { // scrolling down, and we're jumping more than 0.5 of a screen + m_scrollOffset = (offset - range) * size; + } + m_scrollSpeed = (offset * size - m_scrollOffset) / m_scrollTime; + if (!m_wasReset) + { + g_infoManager.SetContainerMoving(GetID(), offset - m_offset); + if (m_scrollSpeed) + m_scrollTimer.Start(); + else + m_scrollTimer.Stop(); + } + m_offset = offset; +} + +void CGUIBaseContainer::UpdateScrollOffset() +{ + m_scrollOffset += m_scrollSpeed * (m_renderTime - m_scrollLastTime); + if ((m_scrollSpeed < 0 && m_scrollOffset < m_offset * m_layout->Size(m_orientation)) || + (m_scrollSpeed > 0 && m_scrollOffset > m_offset * m_layout->Size(m_orientation))) + { + m_scrollOffset = m_offset * m_layout->Size(m_orientation); + m_scrollSpeed = 0; + m_scrollTimer.Stop(); + } + m_scrollLastTime = m_renderTime; +} + +int CGUIBaseContainer::CorrectOffset(int offset, int cursor) const +{ + return offset + cursor; +} + +void CGUIBaseContainer::Reset() +{ + m_wasReset = true; + m_items.clear(); + m_lastItem = NULL; +} + +void CGUIBaseContainer::LoadLayout(TiXmlElement *layout) +{ + TiXmlElement *itemElement = layout->FirstChildElement("itemlayout"); + while (itemElement) + { // we have a new item layout + CGUIListItemLayout itemLayout; + itemLayout.LoadLayout(itemElement, false); + m_layouts.push_back(itemLayout); + itemElement = itemElement->NextSiblingElement("itemlayout"); + } + itemElement = layout->FirstChildElement("focusedlayout"); + while (itemElement) + { // we have a new item layout + CGUIListItemLayout itemLayout; + itemLayout.LoadLayout(itemElement, true); + m_focusedLayouts.push_back(itemLayout); + itemElement = itemElement->NextSiblingElement("focusedlayout"); + } +} + +void CGUIBaseContainer::LoadContent(TiXmlElement *content) +{ + TiXmlElement *root = content->FirstChildElement("content"); + if (!root) + return; + + g_SkinInfo.ResolveIncludes(root); + + vector<CGUIListItemPtr> items; + TiXmlElement *item = root->FirstChildElement("item"); + while (item) + { + // format: + // <item label="Cool Video" label2="" thumb="mythumb.png">PlayMedia(c:\videos\cool_video.avi)</item> + // <item label="My Album" label2="" thumb="whatever.jpg">ActivateWindow(MyMusic,c:\music\my album)</item> + // <item label="Apple Movie Trailers" label2="Bob" thumb="foo.tbn">RunScript(special://xbmc/scripts/apple movie trailers/default.py)</item> + + // OR the more verbose, but includes-friendly: + // <item> + // <label>blah</label> + // <label2>foo</label2> + // <thumb>bar.png</thumb> + // <icon>foo.jpg</icon> + // <onclick>ActivateWindow(Home)</onclick> + // </item> + g_SkinInfo.ResolveIncludes(item); + if (item->FirstChild()) + { + CFileItemPtr newItem; + // check whether we're using the more verbose method... + TiXmlNode *click = item->FirstChild("onclick"); + if (click && click->FirstChild()) + { + CStdString label, label2, thumb, icon; + XMLUtils::GetString(item, "label", label); label = CGUIControlFactory::FilterLabel(label); + XMLUtils::GetString(item, "label2", label2); label2 = CGUIControlFactory::FilterLabel(label2); + XMLUtils::GetString(item, "thumb", thumb); thumb = CGUIControlFactory::FilterLabel(thumb); + XMLUtils::GetString(item, "icon", icon); icon = CGUIControlFactory::FilterLabel(icon); + const char *id = item->Attribute("id"); + int visibleCondition = 0; + CGUIControlFactory::GetConditionalVisibility(item, visibleCondition); + newItem.reset(new CFileItem(CGUIInfoLabel::GetLabel(label))); + // multiple action strings are concat'd together, separated with " , " + vector<CGUIActionDescriptor> actions; + CGUIControlFactory::GetMultipleString(item, "onclick", actions); + newItem->m_strPath = ""; + for (vector<CGUIActionDescriptor>::iterator it = actions.begin(); it != actions.end(); ++it) + { + (*it).m_action.Replace(",", ",,"); + if (newItem->m_strPath.length() > 0) + { + newItem->m_strPath += " , "; + } + newItem->m_strPath += (*it).m_action; + } + newItem->SetLabel2(CGUIInfoLabel::GetLabel(label2)); + newItem->SetThumbnailImage(CGUIInfoLabel::GetLabel(thumb, true)); + newItem->SetIconImage(CGUIInfoLabel::GetLabel(icon, true)); + if (label.Find("$INFO") >= 0) newItem->SetProperty("label", label); + if (label2.Find("$INFO") >= 0) newItem->SetProperty("label2", label2); + if (icon.Find("$INFO") >= 0) newItem->SetProperty("icon", icon); + if (thumb.Find("$INFO") >= 0) newItem->SetProperty("thumb", thumb); + if (id) newItem->m_iprogramCount = atoi(id); + newItem->m_idepth = visibleCondition; + } + else + { + CStdString label, label2, thumb, icon; + label = item->Attribute("label"); label = CGUIControlFactory::FilterLabel(label); + label2 = item->Attribute("label2"); label2 = CGUIControlFactory::FilterLabel(label2); + thumb = item->Attribute("thumb"); thumb = CGUIControlFactory::FilterLabel(thumb); + icon = item->Attribute("icon"); icon = CGUIControlFactory::FilterLabel(icon); + const char *id = item->Attribute("id"); + newItem.reset(new CFileItem(CGUIInfoLabel::GetLabel(label))); + newItem->m_strPath = item->FirstChild()->Value(); + newItem->SetLabel2(CGUIInfoLabel::GetLabel(label2)); + newItem->SetThumbnailImage(CGUIInfoLabel::GetLabel(thumb, true)); + newItem->SetIconImage(CGUIInfoLabel::GetLabel(icon, true)); + if (id) newItem->m_iprogramCount = atoi(id); + newItem->m_idepth = 0; // no visibility condition + } + items.push_back(newItem); + } + item = item->NextSiblingElement("item"); + } + SetStaticContent(items); +} + +void CGUIBaseContainer::SetStaticContent(const vector<CGUIListItemPtr> &items) +{ + m_staticContent = true; + m_staticUpdateTime = 0; + m_staticItems.clear(); + m_staticItems.assign(items.begin(), items.end()); + UpdateVisibility(); +} + +void CGUIBaseContainer::SetType(VIEW_TYPE type, const CStdString &label) +{ + m_type = type; + m_label = label; +} + +void CGUIBaseContainer::MoveToItem(int item) +{ + g_infoManager.SetContainerMoving(GetID(), item - m_cursor); + m_cursor = item; +} + +void CGUIBaseContainer::FreeMemory(int keepStart, int keepEnd) +{ + if (keepStart < keepEnd) + { // remove before keepStart and after keepEnd + for (int i = 0; i < keepStart && i < (int)m_items.size(); ++i) + m_items[i]->FreeMemory(); + for (int i = keepEnd + 1; i < (int)m_items.size(); ++i) + m_items[i]->FreeMemory(); + } + else + { // wrapping + for (int i = keepEnd + 1; i < keepStart && i < (int)m_items.size(); ++i) + m_items[i]->FreeMemory(); + } +} + +bool CGUIBaseContainer::InsideLayout(const CGUIListItemLayout *layout, const CPoint &point) +{ + if (!layout) return false; + if ((m_orientation == VERTICAL && layout->Size(HORIZONTAL) && point.x > layout->Size(HORIZONTAL)) || + (m_orientation == HORIZONTAL && layout->Size(VERTICAL) && point.y > layout->Size(VERTICAL))) + return false; + return true; +} + +#ifdef _DEBUG +void CGUIBaseContainer::DumpTextureUse() +{ + CLog::Log(LOGDEBUG, "%s for container %u", __FUNCTION__, GetID()); + for (unsigned int i = 0; i < m_items.size(); ++i) + { + CGUIListItemPtr item = m_items[i]; + if (item->GetFocusedLayout()) item->GetFocusedLayout()->DumpTextureUse(); + if (item->GetLayout()) item->GetLayout()->DumpTextureUse(); + } +} +#endif + +bool CGUIBaseContainer::GetCondition(int condition, int data) const +{ + switch (condition) + { + case CONTAINER_ROW: + return (m_orientation == VERTICAL) ? (m_cursor == data) : true; + case CONTAINER_COLUMN: + return (m_orientation == HORIZONTAL) ? (m_cursor == data) : true; + case CONTAINER_POSITION: + return (m_cursor == data); + case CONTAINER_HAS_NEXT: + return (HasNextPage()); + case CONTAINER_HAS_PREVIOUS: + return (HasPreviousPage()); + case CONTAINER_SUBITEM: + { + CGUIListItemLayout *layout = GetFocusedLayout(); + return layout ? (layout->GetFocusedItem() == (unsigned int)data) : false; + } + case CONTAINER_SCROLLING: + return (m_scrollTimer.GetElapsedMilliseconds() > m_scrollTime || m_pageChangeTimer.IsRunning()); + default: + return false; + } +} + +void CGUIBaseContainer::GetCurrentLayouts() +{ + m_layout = NULL; + for (unsigned int i = 0; i < m_layouts.size(); i++) + { + int condition = m_layouts[i].GetCondition(); + if (!condition || g_infoManager.GetBool(condition, GetParentID())) + { + m_layout = &m_layouts[i]; + break; + } + } + if (!m_layout && m_layouts.size()) + m_layout = &m_layouts[0]; // failsafe + + m_focusedLayout = NULL; + for (unsigned int i = 0; i < m_focusedLayouts.size(); i++) + { + int condition = m_focusedLayouts[i].GetCondition(); + if (!condition || g_infoManager.GetBool(condition, GetParentID())) + { + m_focusedLayout = &m_focusedLayouts[i]; + break; + } + } + if (!m_focusedLayout && m_focusedLayouts.size()) + m_focusedLayout = &m_focusedLayouts[0]; // failsafe +} + +bool CGUIBaseContainer::HasNextPage() const +{ + return false; +} + +bool CGUIBaseContainer::HasPreviousPage() const +{ + return false; +} + +CStdString CGUIBaseContainer::GetLabel(int info) const +{ + CStdString label; + switch (info) + { + case CONTAINER_NUM_PAGES: + label.Format("%u", (GetRows() + m_itemsPerPage - 1) / m_itemsPerPage); + break; + case CONTAINER_CURRENT_PAGE: + label.Format("%u", GetCurrentPage()); + break; + case CONTAINER_POSITION: + label.Format("%i", m_cursor); + break; + case CONTAINER_NUM_ITEMS: + { + unsigned int numItems = GetNumItems(); + if (numItems && m_items[0]->IsFileItem() && (boost::static_pointer_cast<CFileItem>(m_items[0]))->IsParentFolder()) + label.Format("%u", numItems-1); + else + label.Format("%u", numItems); + } + break; + default: + break; + } + return label; +} + +int CGUIBaseContainer::GetCurrentPage() const +{ + if (m_offset + m_itemsPerPage >= (int)GetRows()) // last page + return (GetRows() + m_itemsPerPage - 1) / m_itemsPerPage; + return m_offset / m_itemsPerPage + 1; +} + +void CGUIBaseContainer::GetCacheOffsets(int &cacheBefore, int &cacheAfter) +{ + if (m_scrollSpeed > 0) + { + cacheBefore = 0; + cacheAfter = m_cacheItems; + } + else if (m_scrollSpeed < 0) + { + cacheBefore = m_cacheItems; + cacheAfter = 0; + } + else + { + cacheBefore = m_cacheItems / 2; + cacheAfter = m_cacheItems / 2; + } +} diff --git a/guilib/GUIBaseContainer.h b/guilib/GUIBaseContainer.h new file mode 100644 index 0000000000..7a0fe0cd2d --- /dev/null +++ b/guilib/GUIBaseContainer.h @@ -0,0 +1,181 @@ +/*! +\file GUIListContainer.h +\brief +*/ + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControl.h" +#include "GUIListItemLayout.h" +#include "boost/shared_ptr.hpp" +#include "utils/Stopwatch.h" + +typedef boost::shared_ptr<CGUIListItem> CGUIListItemPtr; + +/*! + \ingroup controls + \brief + */ + +class CGUIBaseContainer : public CGUIControl +{ +public: + CGUIBaseContainer(int parentID, int controlID, float posX, float posY, float width, float height, ORIENTATION orientation, int scrollTime, int preloadItems); + virtual ~CGUIBaseContainer(void); + + virtual bool OnAction(const CAction &action); + virtual void OnDown(); + virtual void OnUp(); + virtual void OnLeft(); + virtual void OnRight(); + virtual bool OnMouseOver(const CPoint &point); + virtual bool OnMouseClick(int button, const CPoint &point); + virtual bool OnMouseDoubleClick(int button, const CPoint &point); + virtual bool OnMouseWheel(char wheel, const CPoint &point); + virtual bool OnMessage(CGUIMessage& message); + virtual void SetFocus(bool bOnOff); + virtual void AllocResources(); + virtual void FreeResources(); + virtual void UpdateVisibility(const CGUIListItem *item = NULL); + + virtual unsigned int GetRows() const; + + virtual bool HasNextPage() const; + virtual bool HasPreviousPage() const; + + void SetPageControl(int id); + + virtual CStdString GetDescription() const; + virtual void SaveStates(std::vector<CControlState> &states); + virtual int GetSelectedItem() const; + + virtual void DoRender(DWORD currentTime); + void LoadLayout(TiXmlElement *layout); + void LoadContent(TiXmlElement *content); + + VIEW_TYPE GetType() const { return m_type; }; + const CStdString &GetLabel() const { return m_label; }; + void SetType(VIEW_TYPE type, const CStdString &label); + + virtual bool IsContainer() const { return true; }; + CGUIListItemPtr GetListItem(int offset, unsigned int flag = 0) const; + + virtual bool GetCondition(int condition, int data) const; + CStdString GetLabel(int info) const; + + void SetStaticContent(const std::vector<CGUIListItemPtr> &items); + +#ifdef _DEBUG + virtual void DumpTextureUse(); +#endif +protected: + bool OnClick(int actionID); + virtual bool SelectItemFromPoint(const CPoint &point); + virtual void Render(); + virtual void RenderItem(float posX, float posY, CGUIListItem *item, bool focused); + virtual void Scroll(int amount); + virtual bool MoveDown(bool wrapAround); + virtual bool MoveUp(bool wrapAround); + virtual void MoveToItem(int item); + virtual void ValidateOffset(); + virtual int CorrectOffset(int offset, int cursor) const; + virtual void UpdateLayout(bool refreshAllItems = false); + virtual void SetPageControlRange(); + virtual void UpdatePageControl(int offset); + virtual void CalculateLayout(); + virtual void SelectItem(int item) {}; + virtual void Reset(); + virtual unsigned int GetNumItems() const { return m_items.size(); }; + virtual int GetCurrentPage() const; + bool InsideLayout(const CGUIListItemLayout *layout, const CPoint &point); + + inline float Size() const; + void MoveToRow(int row); + void FreeMemory(int keepStart, int keepEnd); + void GetCurrentLayouts(); + CGUIListItemLayout *GetFocusedLayout() const; + + int m_offset; + int m_cursor; + float m_analogScrollCount; + unsigned int m_lastHoldTime; + + ORIENTATION m_orientation; + int m_itemsPerPage; + + std::vector< CGUIListItemPtr > m_items; + typedef std::vector<CGUIListItemPtr> ::iterator iItems; + CGUIListItem *m_lastItem; + + int m_pageControl; + + DWORD m_renderTime; + + std::vector<CGUIListItemLayout> m_layouts; + std::vector<CGUIListItemLayout> m_focusedLayouts; + + CGUIListItemLayout *m_layout; + CGUIListItemLayout *m_focusedLayout; + + virtual void ScrollToOffset(int offset); + void UpdateScrollOffset(); + + DWORD m_scrollLastTime; + int m_scrollTime; + float m_scrollOffset; + + VIEW_TYPE m_type; + CStdString m_label; + + bool m_staticContent; + DWORD m_staticUpdateTime; + std::vector<CGUIListItemPtr> m_staticItems; + bool m_wasReset; // true if we've received a Reset message until we've rendered once. Allows + // us to make sure we don't tell the infomanager that we've been moving when + // the "movement" was simply due to the list being repopulated (thus cursor position + // changing around) + + void UpdateScrollByLetter(); + void GetCacheOffsets(int &cacheBefore, int &cacheAfter); + bool ScrollingDown() const { return m_scrollSpeed > 0; }; + bool ScrollingUp() const { return m_scrollSpeed < 0; }; + void OnNextLetter(); + void OnPrevLetter(); + void OnJumpLetter(char letter); + void OnJumpSMS(int letter); + std::vector< std::pair<int, CStdString> > m_letterOffsets; +private: + int m_cacheItems; + float m_scrollSpeed; + CStopWatch m_scrollTimer; + CStopWatch m_pageChangeTimer; + + // letter match searching + CStopWatch m_matchTimer; + CStdString m_match; + + static const int letter_match_timeout = 1000; +}; + + diff --git a/guilib/GUIBorderedImage.cpp b/guilib/GUIBorderedImage.cpp new file mode 100644 index 0000000000..6ba1c3d931 --- /dev/null +++ b/guilib/GUIBorderedImage.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIBorderedImage.h" + +CGUIBorderedImage::CGUIBorderedImage(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& texture, const CTextureInfo& borderTexture, const FRECT &borderSize) + : CGUIImage(parentID, controlID, posX + borderSize.left, posY + borderSize.top, width - borderSize.left - borderSize.right, height - borderSize.top - borderSize.bottom, texture), + m_borderImage(posX, posY, width, height, borderTexture) +{ + memcpy(&m_borderSize, &borderSize, sizeof(FRECT)); + ControlType = GUICONTROL_BORDEREDIMAGE; +} + +CGUIBorderedImage::CGUIBorderedImage(const CGUIBorderedImage &right) +: CGUIImage(right), m_borderImage(right.m_borderImage) +{ + memcpy(&m_borderSize, &right.m_borderSize, sizeof(FRECT)); + ControlType = GUICONTROL_BORDEREDIMAGE; +} + +CGUIBorderedImage::~CGUIBorderedImage(void) +{ +} + +void CGUIBorderedImage::Render() +{ + if (!m_borderImage.GetFileName().IsEmpty() && m_texture.IsAllocated()) + { + CRect rect = CRect(m_texture.GetXPosition(), m_texture.GetYPosition(), m_texture.GetXPosition() + m_texture.GetWidth(), m_texture.GetYPosition() + m_texture.GetHeight()); + rect.Intersect(m_texture.GetRenderRect()); + m_borderImage.SetPosition(rect.x1 - m_borderSize.left, rect.y1 - m_borderSize.top); + m_borderImage.SetWidth(rect.Width() + m_borderSize.left + m_borderSize.right); + m_borderImage.SetHeight(rect.Height() + m_borderSize.top + m_borderSize.bottom); + m_borderImage.Render(); + } + CGUIImage::Render(); +} + +void CGUIBorderedImage::AllocResources() +{ + m_borderImage.AllocResources(); + CGUIImage::AllocResources(); +} + +void CGUIBorderedImage::FreeResources() +{ + m_borderImage.FreeResources(); + CGUIImage::FreeResources(); +} + +void CGUIBorderedImage::DynamicResourceAlloc(bool bOnOff) +{ + m_borderImage.DynamicResourceAlloc(bOnOff); + CGUIImage::DynamicResourceAlloc(bOnOff); +} diff --git a/guilib/GUIBorderedImage.h b/guilib/GUIBorderedImage.h new file mode 100644 index 0000000000..535b1a00d6 --- /dev/null +++ b/guilib/GUIBorderedImage.h @@ -0,0 +1,47 @@ +#ifndef CGUIBorderedImage_H +#define CGUIBorderedImage_H + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControl.h" +#include "TextureManager.h" +#include "GUIImage.h" + +class CGUIBorderedImage : public CGUIImage +{ +public: + CGUIBorderedImage(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& texture, const CTextureInfo& borderTexture, const FRECT &borderSize); + CGUIBorderedImage(const CGUIBorderedImage &right); + virtual ~CGUIBorderedImage(void); + virtual CGUIBorderedImage *Clone() const { return new CGUIBorderedImage(*this); }; + + virtual void Render(); + virtual void AllocResources(); + virtual void FreeResources(); + virtual void DynamicResourceAlloc(bool bOnOff); + +protected: + CGUITexture m_borderImage; + FRECT m_borderSize; +}; + +#endif diff --git a/guilib/GUIButtonControl.cpp b/guilib/GUIButtonControl.cpp new file mode 100644 index 0000000000..6c52bb7991 --- /dev/null +++ b/guilib/GUIButtonControl.cpp @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIButtonControl.h" +#include "GUIWindowManager.h" +#include "GUIDialog.h" +#include "utils/CharsetConverter.h" +#include "GUIFontManager.h" +#include "MouseStat.h" + +using namespace std; + +CGUIButtonControl::CGUIButtonControl(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& textureFocus, const CTextureInfo& textureNoFocus, const CLabelInfo& labelInfo) + : CGUIControl(parentID, controlID, posX, posY, width, height) + , m_imgFocus(posX, posY, width, height, textureFocus) + , m_imgNoFocus(posX, posY, width, height, textureNoFocus) + , m_textLayout(labelInfo.font, false), m_textLayout2(labelInfo.font, false) +{ + m_bSelected = false; + m_alpha = 255; + m_dwFocusCounter = 0; + m_label = labelInfo; + ControlType = GUICONTROL_BUTTON; +} + +CGUIButtonControl::~CGUIButtonControl(void) +{ +} + +void CGUIButtonControl::Render() +{ + if (m_bInvalidated) + { + m_imgFocus.SetWidth(m_width); + m_imgFocus.SetHeight(m_height); + + m_imgNoFocus.SetWidth(m_width); + m_imgNoFocus.SetHeight(m_height); + } + + if (HasFocus()) + { + if (m_pulseOnSelect) + { + DWORD dwAlphaCounter = m_dwFocusCounter + 2; + DWORD dwAlphaChannel; + if ((dwAlphaCounter % 128) >= 64) + dwAlphaChannel = dwAlphaCounter % 64; + else + dwAlphaChannel = 63 - (dwAlphaCounter % 64); + + dwAlphaChannel += 192; + dwAlphaChannel = (DWORD)((float)m_alpha * (float)dwAlphaChannel / 255.0f); + m_imgFocus.SetAlpha((unsigned char)dwAlphaChannel); + } + m_imgFocus.SetVisible(true); + m_imgNoFocus.SetVisible(false); + m_dwFocusCounter++; + } + else + { + m_imgFocus.SetVisible(false); + m_imgNoFocus.SetVisible(true); + } + // render both so the visibility settings cause the frame counter to resetcorrectly + m_imgFocus.Render(); + m_imgNoFocus.Render(); + + RenderText(); + CGUIControl::Render(); +} + +void CGUIButtonControl::RenderText() +{ + m_textLayout.Update(m_info.GetLabel(m_parentID)); + + float fPosX = m_posX + m_label.offsetX; + float fPosY = m_posY + m_label.offsetY; + + if (m_label.align & XBFONT_RIGHT) + fPosX = m_posX + m_width - m_label.offsetX; + + if (m_label.align & XBFONT_CENTER_X) + fPosX = m_posX + m_width / 2; + + if (m_label.align & XBFONT_CENTER_Y) + fPosY = m_posY + m_height / 2; + + if (IsDisabled()) + m_textLayout.Render( fPosX, fPosY, m_label.angle, m_label.disabledColor, m_label.shadowColor, m_label.align, m_label.width, true); + else if (HasFocus() && m_label.focusedColor) + m_textLayout.Render( fPosX, fPosY, m_label.angle, m_label.focusedColor, m_label.shadowColor, m_label.align, m_label.width); + else + m_textLayout.Render( fPosX, fPosY, m_label.angle, m_label.textColor, m_label.shadowColor, m_label.align, m_label.width); + + // render the second label if it exists + CStdString label2(m_info2.GetLabel(m_parentID)); + if (!label2.IsEmpty()) + { + float textWidth, textHeight; + m_textLayout.GetTextExtent(textWidth, textHeight); + m_textLayout2.Update(label2); + + float width = m_width - 2 * m_label.offsetX - textWidth - 5; + if (width < 0) width = 0; + fPosX = m_posX + m_width - m_label.offsetX; + uint32_t dwAlign = XBFONT_RIGHT | (m_label.align & XBFONT_CENTER_Y) | XBFONT_TRUNCATED; + + if (IsDisabled() ) + m_textLayout2.Render( fPosX, fPosY, m_label.angle, m_label.disabledColor, m_label.shadowColor, dwAlign, width, true); + else if (HasFocus() && m_label.focusedColor) + m_textLayout2.Render( fPosX, fPosY, m_label.angle, m_label.focusedColor, m_label.shadowColor, dwAlign, width); + else + m_textLayout2.Render( fPosX, fPosY, m_label.angle, m_label.textColor, m_label.shadowColor, dwAlign, width); + } +} + +bool CGUIButtonControl::OnAction(const CAction &action) +{ + if (action.id == ACTION_SELECT_ITEM) + { + OnClick(); + return true; + } + return CGUIControl::OnAction(action); +} + +bool CGUIButtonControl::OnMessage(CGUIMessage& message) +{ + if (message.GetControlId() == GetID()) + { + if (message.GetMessage() == GUI_MSG_LABEL_SET) + { + SetLabel(message.GetLabel()); + return true; + } + if (message.GetMessage() == GUI_MSG_LABEL2_SET) + { + SetLabel2(message.GetLabel()); + return true; + } + if (message.GetMessage() == GUI_MSG_SELECTED) + { + m_bSelected = true; + return true; + } + if (message.GetMessage() == GUI_MSG_DESELECTED) + { + m_bSelected = false; + return true; + } + } + + return CGUIControl::OnMessage(message); +} + +void CGUIButtonControl::AllocResources() +{ + CGUIControl::AllocResources(); + m_dwFocusCounter = 0; + m_imgFocus.AllocResources(); + m_imgNoFocus.AllocResources(); + if (!m_width) + m_width = m_imgFocus.GetWidth(); + if (!m_height) + m_height = m_imgFocus.GetHeight(); +} + +void CGUIButtonControl::FreeResources() +{ + CGUIControl::FreeResources(); + m_imgFocus.FreeResources(); + m_imgNoFocus.FreeResources(); +} + +void CGUIButtonControl::DynamicResourceAlloc(bool bOnOff) +{ + CGUIControl::DynamicResourceAlloc(bOnOff); + m_imgFocus.DynamicResourceAlloc(bOnOff); + m_imgNoFocus.DynamicResourceAlloc(bOnOff); +} + +void CGUIButtonControl::SetLabel(const string &label) +{ // NOTE: No fallback for buttons at this point + m_info.SetLabel(label, ""); +} + +void CGUIButtonControl::SetLabel2(const string &label2) +{ // NOTE: No fallback for buttons at this point + m_info2.SetLabel(label2, ""); +} + +void CGUIButtonControl::SetPosition(float posX, float posY) +{ + CGUIControl::SetPosition(posX, posY); + + m_imgFocus.SetPosition(posX, posY); + m_imgNoFocus.SetPosition(posX, posY); +} + +void CGUIButtonControl::SetAlpha(unsigned char alpha) +{ + m_alpha = alpha; + m_imgFocus.SetAlpha(alpha); + m_imgNoFocus.SetAlpha(alpha); +} + +void CGUIButtonControl::UpdateColors() +{ + m_label.UpdateColors(); + CGUIControl::UpdateColors(); + m_imgFocus.SetDiffuseColor(m_diffuseColor); + m_imgNoFocus.SetDiffuseColor(m_diffuseColor); +} + +bool CGUIButtonControl::OnMouseClick(int button, const CPoint &point) +{ + if (button == MOUSE_LEFT_BUTTON) + { + g_Mouse.SetState(MOUSE_STATE_CLICK); + CAction action; + action.id = ACTION_SELECT_ITEM; + OnAction(action); + return true; + } + return false; +} + +CStdString CGUIButtonControl::GetDescription() const +{ + CStdString strLabel(m_info.GetLabel(m_parentID)); + return strLabel; +} + +CStdString CGUIButtonControl::GetLabel2() const +{ + CStdString strLabel(m_info2.GetLabel(m_parentID)); + return strLabel; +} + +void CGUIButtonControl::PythonSetLabel(const CStdString &strFont, const string &strText, color_t textColor, color_t shadowColor, color_t focusedColor) +{ + m_label.font = g_fontManager.GetFont(strFont); + m_label.textColor = textColor; + m_label.focusedColor = focusedColor; + m_label.shadowColor = shadowColor; + SetLabel(strText); +} + +void CGUIButtonControl::PythonSetDisabledColor(color_t disabledColor) +{ + m_label.disabledColor = disabledColor; +} + +void CGUIButtonControl::RAMSetTextColor(color_t textColor) +{ + m_label.textColor = textColor; +} + +void CGUIButtonControl::SettingsCategorySetTextAlign(uint32_t align) +{ + m_label.align = align; +} + +void CGUIButtonControl::OnClick() +{ + // Save values, as the click message may deactivate the window + int controlID = GetID(); + int parentID = GetParentID(); + vector<CGUIActionDescriptor> clickActions = m_clickActions; + + // button selected, send a message + CGUIMessage msg(GUI_MSG_CLICKED, controlID, parentID, 0); + SendWindowMessage(msg); + + // and execute our actions + for (unsigned int i = 0; i < clickActions.size(); i++) + { + CGUIMessage message(GUI_MSG_EXECUTE, controlID, parentID); + message.SetAction(clickActions[i]); + g_graphicsContext.SendMessage(message); + } +} + +void CGUIButtonControl::OnFocus() +{ + for (unsigned int i = 0; i < m_focusActions.size(); i++) + { + CGUIMessage message(GUI_MSG_EXECUTE, m_controlID, m_parentID); + message.SetAction(m_focusActions[i]); + m_gWindowManager.SendThreadMessage(message); + } +} + +void CGUIButtonControl::OnUnFocus() +{ + for (unsigned int i = 0; i < m_unfocusActions.size(); i++) + { + CGUIMessage message(GUI_MSG_EXECUTE, m_controlID, m_parentID); + message.SetAction(m_unfocusActions[i]); + m_gWindowManager.SendThreadMessage(message); + } +} + +void CGUIButtonControl::SetSelected(bool bSelected) +{ + if (m_bSelected != bSelected) + { + m_bSelected = bSelected; + SetInvalid(); + } +} + diff --git a/guilib/GUIButtonControl.h b/guilib/GUIButtonControl.h new file mode 100644 index 0000000000..8602bd954b --- /dev/null +++ b/guilib/GUIButtonControl.h @@ -0,0 +1,103 @@ +/*! +\file GUIButtonControl.h +\brief +*/ + +#ifndef GUILIB_GUIBUTTONCONTROL_H +#define GUILIB_GUIBUTTONCONTROL_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUITexture.h" +#include "GUILabelControl.h" // for CInfoPortion + +/*! + \ingroup controls + \brief + */ +class CGUIButtonControl : public CGUIControl +{ +public: + CGUIButtonControl(int parentID, int controlID, + float posX, float posY, float width, float height, + const CTextureInfo& textureFocus, const CTextureInfo& textureNoFocus, + const CLabelInfo &label); + + virtual ~CGUIButtonControl(void); + virtual CGUIButtonControl *Clone() const { return new CGUIButtonControl(*this); }; + + virtual void Render(); + virtual bool OnAction(const CAction &action) ; + virtual bool OnMouseClick(int button, const CPoint &point); + virtual bool OnMessage(CGUIMessage& message); + virtual void AllocResources(); + virtual void FreeResources(); + virtual void DynamicResourceAlloc(bool bOnOff); + virtual void SetPosition(float posX, float posY); + virtual void SetLabel(const std::string & aLabel); + virtual void SetLabel2(const std::string & aLabel2); + void SetClickActions(const std::vector<CGUIActionDescriptor>& clickActions) { m_clickActions = clickActions; }; + const std::vector<CGUIActionDescriptor> &GetClickActions() const { return m_clickActions; }; + void SetFocusActions(const std::vector<CGUIActionDescriptor>& focusActions) { m_focusActions = focusActions; }; + void SetUnFocusActions(const std::vector<CGUIActionDescriptor>& unfocusActions) { m_unfocusActions = unfocusActions; }; + const CLabelInfo& GetLabelInfo() const { return m_label; }; + virtual CStdString GetLabel() const { return GetDescription(); }; + virtual CStdString GetLabel2() const; + void SetSelected(bool bSelected); + virtual CStdString GetDescription() const; + void SetAlpha(unsigned char alpha); + + void PythonSetLabel(const CStdString &strFont, const std::string &strText, color_t textColor, color_t shadowColor, color_t focusedColor); + void PythonSetDisabledColor(color_t disabledColor); + + void RAMSetTextColor(color_t textColor); + void SettingsCategorySetTextAlign(uint32_t align); + + virtual void OnClick(); + bool HasClickActions() { return m_clickActions.size() > 0; }; + + virtual void UpdateColors(); +protected: + void OnFocus(); + void OnUnFocus(); + virtual void RenderText(); + + CGUITexture m_imgFocus; + CGUITexture m_imgNoFocus; + DWORD m_dwFocusCounter; + unsigned char m_alpha; + + CGUIInfoLabel m_info; + CGUIInfoLabel m_info2; + CLabelInfo m_label; + CGUITextLayout m_textLayout; + CGUITextLayout m_textLayout2; + + std::vector<CGUIActionDescriptor> m_clickActions; + std::vector<CGUIActionDescriptor> m_focusActions; + std::vector<CGUIActionDescriptor> m_unfocusActions; + + bool m_bSelected; +}; +#endif diff --git a/guilib/GUIButtonScroller.cpp b/guilib/GUIButtonScroller.cpp new file mode 100644 index 0000000000..1bfc096c5b --- /dev/null +++ b/guilib/GUIButtonScroller.cpp @@ -0,0 +1,951 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIButtonScroller.h" +#include "GUITextLayout.h" +#include "LocalizeStrings.h" +#include "GUIWindowManager.h" +#include "utils/CharsetConverter.h" +#include "utils/GUIInfoManager.h" +#include "SkinInfo.h" +#include "StringUtils.h" +#include "GUIControlFactory.h" +#include "MouseStat.h" +#include "tinyXML/tinyxml.h" +#include "Key.h" + +using namespace std; + +#define SCROLL_SPEED 6.0f +#define ANALOG_SCROLL_START 0.8f + +CGUIButtonScroller::CGUIButtonScroller(int parentID, int controlID, float posX, float posY, float width, float height, float gap, int iSlots, int iDefaultSlot, int iMovementRange, bool bHorizontal, int iAlpha, bool bWrapAround, bool bSmoothScrolling, const CTextureInfo& textureFocus, const CTextureInfo& textureNoFocus, const CLabelInfo& labelInfo) + : CGUIControl(parentID, controlID, posX, posY, width, height) + , m_imgFocus(posX, posY, width, height, textureFocus) + , m_imgNoFocus(posX, posY, width, height, textureNoFocus) +{ + m_iXMLNumSlots = iSlots; + m_iXMLDefaultSlot = iDefaultSlot - 1; + m_xmlPosX = posX; + m_xmlPosY = posY; + m_xmlWidth = width; + m_xmlHeight = height; + m_buttonGap = gap; + m_iNumSlots = iSlots; + m_bHorizontal = bHorizontal; + m_iDefaultSlot = iDefaultSlot - 1; + m_iMovementRange = iMovementRange; + m_iAlpha = iAlpha; + m_bWrapAround = bWrapAround; + m_bSmoothScrolling = bSmoothScrolling; + m_iSlowScrollCount = 0; + if (!m_bWrapAround) + { // force the amount of movement to allow for the number of slots + if (m_iMovementRange < m_iDefaultSlot) m_iMovementRange = m_iDefaultSlot; + if (m_iMovementRange < m_iNumSlots - 1 - m_iDefaultSlot) m_iMovementRange = m_iNumSlots - 1 - m_iDefaultSlot; + } + // reset other variables to the defaults + m_iCurrentSlot = -1; + m_iOffset = 0; + m_scrollOffset = 0; + m_bScrollUp = false; + m_bScrollDown = false; + m_bMoveUp = false; + m_bMoveDown = false; + m_fAnalogScrollSpeed = 0; + ControlType = GUICONTROL_BUTTONBAR; + // m_dwFrameCounter = 0; + m_label = labelInfo; +} + +CGUIButtonScroller::CGUIButtonScroller(const CGUIButtonScroller &from) +: CGUIControl(from), m_imgFocus(from.m_imgFocus), m_imgNoFocus(from.m_imgNoFocus) +{ + m_iXMLNumSlots = from.m_iXMLNumSlots; + m_iXMLDefaultSlot = from.m_iXMLDefaultSlot; + m_xmlPosX = from.m_xmlPosX; + m_xmlPosY = from.m_xmlPosY; + m_xmlWidth = from.m_xmlWidth; + m_xmlHeight = from.m_xmlHeight; + m_buttonGap = from.m_buttonGap; + m_iNumSlots = from.m_iNumSlots; + m_bHorizontal = from.m_bHorizontal; + m_iDefaultSlot = from.m_iDefaultSlot; + m_iMovementRange = from.m_iMovementRange; + m_iAlpha = from.m_iAlpha; + m_bWrapAround = from.m_bWrapAround; + m_bSmoothScrolling = from.m_bSmoothScrolling; + m_iSlowScrollCount = 0; + // reset other variables to the defaults + m_iCurrentSlot = -1; + m_iOffset = 0; + m_scrollOffset = 0; + m_bScrollUp = false; + m_bScrollDown = false; + m_bMoveUp = false; + m_bMoveDown = false; + m_fAnalogScrollSpeed = 0; + ControlType = GUICONTROL_BUTTONBAR; + // m_dwFrameCounter = 0; + m_label = from.m_label; + // TODO: Clone - copy the buttons across + +} + +CGUIButtonScroller::~CGUIButtonScroller(void) +{} + +bool CGUIButtonScroller::OnAction(const CAction &action) +{ + if (action.id == ACTION_SELECT_ITEM) + { + // send the appropriate message to the parent window + vector<CGUIActionDescriptor> actions = m_vecButtons[GetActiveButton()]->clickActions; + for (unsigned int i = 0; i < actions.size(); i++) + { + CGUIMessage message(GUI_MSG_EXECUTE, GetID(), GetParentID()); + // find our currently highlighted item + message.SetAction(actions[i]); + g_graphicsContext.SendMessage(message); + } + return true; + } + if (action.id == ACTION_CONTEXT_MENU) + { // send a click message to our parent + SEND_CLICK_MESSAGE(GetID(), GetParentID(), action.id); + return true; + } + // smooth scrolling (for analog controls) + if (action.id == ACTION_SCROLL_UP) + { + m_fAnalogScrollSpeed += action.amount1 * action.amount1; + bool handled = false; + while (m_fAnalogScrollSpeed > ANALOG_SCROLL_START) + { + handled = true; + m_fAnalogScrollSpeed -= ANALOG_SCROLL_START; + if (!m_bWrapAround && m_iOffset + m_iCurrentSlot == 0) + break; + DoUp(); + } + return handled; + } + if (action.id == ACTION_SCROLL_DOWN) + { + m_fAnalogScrollSpeed += action.amount1 * action.amount1; + bool handled = false; + while (m_fAnalogScrollSpeed > ANALOG_SCROLL_START) + { + handled = true; + m_fAnalogScrollSpeed -= ANALOG_SCROLL_START; + if (!m_bWrapAround && (unsigned int)(m_iOffset + m_iCurrentSlot) == m_vecButtons.size() - 1) + break; + DoDown(); + } + return handled; + } + return CGUIControl::OnAction(action); +} + +bool CGUIButtonScroller::OnMessage(CGUIMessage &message) +{ + if (message.GetMessage() == GUI_MSG_ITEM_SELECT) + { + SetActiveButton(message.GetParam1()); + } + else if (message.GetMessage() == GUI_MSG_ITEM_SELECTED) + { + message.SetParam1(GetActiveButton()); + return true; + } + return CGUIControl::OnMessage(message); +} + +void CGUIButtonScroller::ClearButtons() +{ + // destroy our buttons (if we have them from a previous viewing) + for (int i = 0; i < (int)m_vecButtons.size(); ++i) + { + CButton* pButton = m_vecButtons[i]; + delete pButton; + } + m_vecButtons.erase(m_vecButtons.begin(), m_vecButtons.end()); +} + +void CGUIButtonScroller::LoadButtons(TiXmlNode *node) +{ + // run through and find all <button> tags + // Format is: + // <button id="1"> + // <label> + // <execute> + // <texturefocus> + // <texturenofocus> + // </button> + + // TODO: UTF-8 - what if the XML encoding is in UTF-8? + TiXmlElement *buttons = node->FirstChildElement("buttons"); + if (!buttons) return; + + // resolve includes + g_SkinInfo.ResolveIncludes(buttons); + + TiXmlElement *buttonNode = buttons->FirstChildElement("button"); + while (buttonNode) + { + // resolve includes + g_SkinInfo.ResolveIncludes(buttonNode); + CButton *button = new CButton; + buttonNode->Attribute("id", &button->id); + const TiXmlNode *childNode = buttonNode->FirstChild("label"); + if (childNode && childNode->FirstChild()) + { + CStdString strLabel = childNode->FirstChild()->Value(); + if (StringUtils::IsNaturalNumber(strLabel)) + button->strLabel = g_localizeStrings.Get(atoi(strLabel.c_str())); + else + { // convert to UTF-8 + CStdString utf8String; + g_charsetConverter.unknownToUTF8(strLabel, utf8String); + button->strLabel = utf8String; + } + } + // get info + childNode = buttonNode->FirstChild("info"); + if (childNode && childNode->FirstChild()) + { + button->info = g_infoManager.TranslateString(childNode->FirstChild()->Value()); + } + childNode = buttonNode->FirstChild("execute"); + if (childNode && childNode->FirstChild()) + { + CGUIActionDescriptor action; + CGUIControlFactory::GetAction((const TiXmlElement*) childNode, action); + button->clickActions.push_back(action); + } + childNode = buttonNode->FirstChild("onclick"); + while (childNode && childNode->FirstChild()) + { + CGUIActionDescriptor action; + CGUIControlFactory::GetAction((const TiXmlElement*) childNode, action); + button->clickActions.push_back(action); + childNode = childNode->NextSibling("onclick"); + } + childNode = buttonNode->FirstChild("texturefocus"); + if (childNode && childNode->FirstChild()) + button->imageFocus = new CGUITexture(m_posX, m_posY, m_width, m_height, (CStdString)childNode->FirstChild()->Value()); + childNode = buttonNode->FirstChild("texturenofocus"); + if (childNode && childNode->FirstChild()) + button->imageNoFocus = new CGUITexture(m_posX, m_posY, m_width, m_height, (CStdString)childNode->FirstChild()->Value()); + m_vecButtons.push_back(button); + buttonNode = buttonNode->NextSiblingElement("button"); + } +} + +void CGUIButtonScroller::AllocResources() +{ + CGUIControl::AllocResources(); + // m_dwFrameCounter=0; + m_imgFocus.AllocResources(); + m_imgNoFocus.AllocResources(); + // calculate our correct width and height + if (m_bHorizontal) + { + m_xmlWidth = (m_iXMLNumSlots * (m_imgFocus.GetWidth() + m_buttonGap) - m_buttonGap); + m_xmlHeight = m_imgFocus.GetHeight(); + } + else + { + m_xmlWidth = m_imgFocus.GetWidth(); + m_xmlHeight = (m_iXMLNumSlots * (m_imgFocus.GetHeight() + m_buttonGap) - m_buttonGap); + } + m_width = m_xmlWidth; + m_height = m_xmlHeight; + // update the number of filled slots etc. + if ((int)m_vecButtons.size() < m_iXMLNumSlots) + { + m_iNumSlots = m_vecButtons.size(); + m_iDefaultSlot = (int)((float)m_iXMLDefaultSlot / ((float)m_iXMLNumSlots - 1) * ((float)m_iNumSlots - 1)); + } + else + { + m_iNumSlots = m_iXMLNumSlots; + m_iDefaultSlot = m_iXMLDefaultSlot; + } + SetActiveButton(0); +} + +void CGUIButtonScroller::FreeResources() +{ + CGUIControl::FreeResources(); + m_imgFocus.FreeResources(); + m_imgNoFocus.FreeResources(); + ClearButtons(); +} + +void CGUIButtonScroller::DynamicResourceAlloc(bool bOnOff) +{ + CGUIControl::DynamicResourceAlloc(bOnOff); + m_imgFocus.DynamicResourceAlloc(bOnOff); + m_imgNoFocus.DynamicResourceAlloc(bOnOff); +} + +void CGUIButtonScroller::Render() +{ + if (m_bInvalidated) + { + if (m_bHorizontal) + { + m_width = m_iNumSlots * (m_imgFocus.GetWidth() + m_buttonGap) - m_buttonGap; + m_height = m_imgFocus.GetHeight(); + m_posX = m_xmlPosX + (m_xmlWidth - m_width) * 0.5f; + m_posY = m_xmlPosY; + } + else + { + m_width = m_imgFocus.GetWidth(); + m_height = m_iNumSlots * (m_imgFocus.GetHeight() + m_buttonGap) - m_buttonGap; + m_posX = m_xmlPosX; + m_posY = m_xmlPosY + (m_xmlHeight - m_height) * 0.5f; + } + } + float posX = m_posX; + float posY = m_posY; + // set our viewport + g_graphicsContext.SetClipRegion(posX, posY, m_width, m_height); + // if we're scrolling, update our scroll offset + if (m_bScrollUp || m_bScrollDown) + { + float maxScroll = m_bHorizontal ? m_imgFocus.GetWidth() : m_imgFocus.GetHeight(); + maxScroll += m_buttonGap; + m_scrollOffset += (int)(maxScroll / m_fScrollSpeed) + 1; + if (m_scrollOffset > maxScroll || !m_bSmoothScrolling) + { + m_scrollOffset = 0; + if (m_bScrollUp) + { + if (GetNext(m_iOffset) != -1) m_iOffset = GetNext(m_iOffset); + } + else + { + if (GetPrevious(m_iOffset) != -1) m_iOffset = GetPrevious(m_iOffset); + } + // check for wraparound... + if (!m_bWrapAround) + { + if (m_iOffset + m_iNumSlots > (int)m_vecButtons.size()) + m_iOffset = GetPrevious(m_iOffset); + } + m_bScrollUp = false; + m_bScrollDown = false; + } + else + { + if (m_bScrollUp) + { + if (m_bHorizontal) + posX -= m_scrollOffset; + else + posY -= m_scrollOffset; + } + else + { + if (m_bHorizontal) + posX += m_scrollOffset - maxScroll; + else + posY += m_scrollOffset - maxScroll; + } + } + } + float posX3 = posX; + float posY3 = posY; + // ok, now check if we're scrolling down + int iOffset = m_iOffset; + if (m_bScrollDown) + { + iOffset = GetPrevious(iOffset); + RenderItem(posX, posY, iOffset, false); + } + // ok, now render the main block + for (int i = 0; i < m_iNumSlots; i++) + RenderItem(posX, posY, iOffset, false); + // ok, now check if we're scrolling up + if (m_bScrollUp) + RenderItem(posX, posY, iOffset, false); + // ok, now render the background slot... + if (HasFocus()) + { + posX = m_posX; + posY = m_posY; + // check if we're moving up or down + if (m_bMoveUp || m_bMoveDown) + { + float maxScroll = m_bHorizontal ? m_imgFocus.GetWidth() : m_imgFocus.GetHeight(); + maxScroll += m_buttonGap; + m_scrollOffset += maxScroll / SCROLL_SPEED + 1; + if (m_scrollOffset > maxScroll || !m_bSmoothScrolling) + { + m_scrollOffset = 0; + if (m_bMoveUp) + { + if (m_iCurrentSlot > 0) + m_iCurrentSlot--; + } + else + { + if (m_iCurrentSlot + 1 < m_iNumSlots) + m_iCurrentSlot++; + } + m_bMoveUp = false; + m_bMoveDown = false; + } + else + { + if (m_bMoveUp) + { + if (m_bHorizontal) + posX -= m_scrollOffset; + else + posY -= m_scrollOffset; + } + else + { + if (m_bHorizontal) + posX += m_scrollOffset; + else + posY += m_scrollOffset; + } + } + } + if (m_bHorizontal) + posX += m_iCurrentSlot * ((int)m_imgFocus.GetWidth() + m_buttonGap); + else + posY += m_iCurrentSlot * ((int)m_imgFocus.GetHeight() + m_buttonGap); + // check if we have a skinner-defined icon image + CGUITexture *pImage = m_vecButtons[GetActiveButton()]->imageFocus; + if (pImage && (m_bScrollUp || m_bScrollDown)) + pImage = NULL; + else if (!pImage) + pImage = &m_imgFocus; + if (pImage) + { + pImage->SetPosition(posX, posY); + pImage->SetVisible(true); + pImage->SetWidth(m_imgFocus.GetWidth()); + pImage->SetHeight(m_imgFocus.GetHeight()); + pImage->Render(); + } + } + // Now render the text + iOffset = m_iOffset; + posX = posX3; + posY = posY3; + if (m_bScrollDown) + { + iOffset = GetPrevious(iOffset); + RenderItem(posX, posY, iOffset, true); + } + // ok, now render the main block + for (int i = 0; i < m_iNumSlots; i++) + RenderItem(posX, posY, iOffset, true); + // ok, now check if we're scrolling up + if (m_bScrollUp) + RenderItem(posX, posY, iOffset, true); + + // reset the viewport + g_graphicsContext.RestoreClipRegion(); + CGUIControl::Render(); +} + +int CGUIButtonScroller::GetNext(int iCurrent) const +{ + if (iCurrent + 1 >= (int)m_vecButtons.size()) + { + if (m_bWrapAround) + return 0; + else + return -1; + } + else + return iCurrent + 1; +} + +int CGUIButtonScroller::GetPrevious(int iCurrent) +{ + if (iCurrent - 1 < 0) + { + if (m_bWrapAround) + return m_vecButtons.size() - 1; + else + return -1; + } + else + return iCurrent -1; +} + +int CGUIButtonScroller::GetButton(int iOffset) +{ + return iOffset % ((int)m_vecButtons.size()); +} + +void CGUIButtonScroller::SetActiveButton(int iButton) +{ + if (iButton >= (int)m_vecButtons.size()) + iButton = 0; + // set the highlighted button + m_iCurrentSlot = m_iDefaultSlot; + // and the appropriate offset + if (!m_bWrapAround) + { // check whether there is no wiggle room + int iMinButton = m_iDefaultSlot - m_iMovementRange; + if (iMinButton < 0) iMinButton = 0; + if (iButton < iMinButton) + { + m_iOffset = 0; + m_iCurrentSlot = iMinButton; + return ; + } + int iMaxButton = m_iDefaultSlot + m_iMovementRange; + if (iMaxButton >= m_iNumSlots) iMaxButton = m_iNumSlots - 1; + if (iButton > iMaxButton) + { + m_iOffset = iButton - iMaxButton; + m_iCurrentSlot = iMaxButton; + return ; + } + // now change our current slot so that it all fits nicely + // lastly, make sure we fill the number of slots that we have (if possible) + int iNumButtonsToShow = m_vecButtons.size() - iButton + m_iCurrentSlot; + if (iNumButtonsToShow < m_iNumSlots && iNumButtonsToShow < (int)m_vecButtons.size()) + { // we have empty space - try and fill it up + while (iNumButtonsToShow < (int)m_vecButtons.size() && m_iCurrentSlot + 1 < m_iNumSlots) + { + m_iCurrentSlot++; + iNumButtonsToShow = m_vecButtons.size() - iButton + m_iCurrentSlot; + } + } + } + m_iOffset = 0; + for (int i = 0; i < (int)m_vecButtons.size(); i++) + { + int iItem = i; + for (int j = 0; j < m_iCurrentSlot; j++) + if (GetNext(iItem) != -1) iItem = GetNext(iItem); + if (iItem == iButton) + { + m_iOffset = i; + break; + } + } +} + +void CGUIButtonScroller::OnUp() +{ + if (m_bHorizontal) + CGUIControl::OnUp(); + else if (!m_bWrapAround && m_iOffset + m_iCurrentSlot == 0) + { + if (m_controlUp != GetID()) + CGUIControl::OnUp(); // not wrapping around, and we're up the top + our next control is different + else + SetActiveButton((int)m_vecButtons.size() - 1); // move to the last button in the list + } + else + DoUp(); +} + +void CGUIButtonScroller::OnDown() +{ + if (m_bHorizontal) + CGUIControl::OnDown(); + else if (!m_bWrapAround && (unsigned int) (m_iOffset + m_iCurrentSlot) == m_vecButtons.size() - 1) + { + if (m_controlUp != GetID()) + CGUIControl::OnDown(); // not wrapping around, and we're down the bottom + our next control is different + else + SetActiveButton(0); // move to the first button in the list + } + else + DoDown(); +} + +void CGUIButtonScroller::OnLeft() +{ + if (!m_bHorizontal) + CGUIControl::OnLeft(); + else if (!m_bWrapAround && m_iOffset + m_iCurrentSlot == 0 && m_controlLeft != GetID()) + CGUIControl::OnLeft(); // not wrapping around, and we're at the left + our next control is different + else + DoUp(); +} + +void CGUIButtonScroller::OnRight() +{ + if (!m_bHorizontal) + CGUIControl::OnRight(); + else if (!m_bWrapAround && (unsigned int) (m_iOffset + m_iCurrentSlot) == m_vecButtons.size() - 1 && m_controlRight != GetID()) + CGUIControl::OnRight(); // not wrapping around, and we're at the right + our next control is different + else + DoDown(); +} + +void CGUIButtonScroller::DoUp() +{ + if (!m_bScrollUp) + { + if (m_iCurrentSlot - 1 < m_iDefaultSlot - m_iMovementRange || m_iCurrentSlot - 1 < 0) + { + if (m_bScrollDown) + { // finish scroll for higher speed + m_bScrollDown = false; + m_scrollOffset = 0; + m_iOffset = GetPrevious(m_iOffset); + } + else + { + m_bScrollDown = true; + m_fScrollSpeed = SCROLL_SPEED; + } + } + else + { + if (m_bMoveUp) + { + m_bMoveUp = false; + m_scrollOffset = 0; + if (m_iCurrentSlot > 0) m_iCurrentSlot--; + } + m_bMoveUp = true; + } + } +} + +void CGUIButtonScroller::DoDown() +{ + if (!m_bScrollDown) + { + if (m_iCurrentSlot + 1 > m_iDefaultSlot + m_iMovementRange || m_iCurrentSlot + 1 >= m_iNumSlots) + if (m_bScrollUp) + { // finish scroll for higher speed + m_bScrollUp = false; + m_scrollOffset = 0; + if (GetNext(m_iOffset) != -1) m_iOffset = GetNext(m_iOffset); + } + else + { + m_bScrollUp = true; + m_fScrollSpeed = SCROLL_SPEED; + } + else + { + if (m_bMoveDown) + { + m_bMoveDown = false; + m_scrollOffset = 0; + if (m_iCurrentSlot + 1 < m_iNumSlots) m_iCurrentSlot++; + } + m_bMoveDown = true; + } + } +} + +void CGUIButtonScroller::UpdateColors() +{ + m_label.UpdateColors(); + CGUIControl::UpdateColors(); +} + +void CGUIButtonScroller::RenderItem(float &posX, float &posY, int &iOffset, bool bText) +{ + if (iOffset < 0) return ; + float fStartAlpha, fEndAlpha; + GetScrollZone(fStartAlpha, fEndAlpha); + if (bText) + { + if (!m_label.font) return ; + float fPosX = posX + m_label.offsetX; + float fPosY = posY + m_label.offsetY; + if (m_label.align & XBFONT_RIGHT) + fPosX = posX + m_imgFocus.GetWidth() - m_label.offsetX; + if (m_label.align & XBFONT_CENTER_X) + fPosX = posX + m_imgFocus.GetWidth() / 2; + if (m_label.align & XBFONT_CENTER_Y) + fPosY = posY + m_imgFocus.GetHeight() / 2; + + // label is from <info> tag first, and if that's blank, + // we use the <label> tag + CStdString label = g_infoManager.GetLabel(m_vecButtons[iOffset]->info); + if (label.IsEmpty()) + label = m_vecButtons[iOffset]->strLabel; + + float fAlpha = 255.0f; + if (m_bHorizontal) + { + if (fPosX < fStartAlpha) + fAlpha -= (fStartAlpha - fPosX) / (fStartAlpha - m_posX) * m_iAlpha * 2.55f; + if (fPosX > fEndAlpha) + fAlpha -= (fPosX - fEndAlpha) / (m_posX + m_width - fEndAlpha) * m_iAlpha * 2.55f; + } + else + { + if (fPosY < fStartAlpha) + fAlpha -= (fStartAlpha - fPosY) / (fStartAlpha - m_posY) * m_iAlpha * 2.55f; + if (fPosY > fEndAlpha) + fAlpha -= (fPosY - fEndAlpha) / (m_posY + m_height - fEndAlpha) * m_iAlpha * 2.55f; + } + if (fAlpha < 1) fAlpha = 1; // don't quite go all the way transparent, + // as any shadow colour will not be rendered transparent if + // it's defined in the font class + if (fAlpha > 255) fAlpha = 255.0f; + color_t alpha = (color_t)(fAlpha + 0.5f); + color_t color = (m_label.focusedColor && iOffset == GetActiveButton()) ? m_label.focusedColor : m_label.textColor; + color_t blendedAlpha = (alpha * ((color & 0xff000000) >> 24)) / 255; + color_t blendedColor = (blendedAlpha << 24) | (color & 0xFFFFFF); + blendedAlpha = (alpha * ((m_label.shadowColor & 0xff000000) >> 24)) / 255; + color_t shadowColor = (alpha << 24) | (m_label.shadowColor & 0xFFFFFF); + CGUITextLayout::DrawText(m_label.font, fPosX, fPosY, blendedColor, shadowColor, label, m_label.align); + } + else + { + float fAlpha = 255.0f; + float fAlpha1 = 255.0f; + // check if we have a skinner-defined texture... + CGUITexture *pImage = m_vecButtons[iOffset]->imageNoFocus; + if (!pImage) pImage = &m_imgNoFocus; + pImage->SetAlpha(0xFF); + pImage->SetVisible(true); + if (m_bHorizontal) + { + if (posX < fStartAlpha) + { + fAlpha -= (fStartAlpha - posX) / (fStartAlpha - m_posX) * m_iAlpha * 2.55f; + fAlpha1 -= (fStartAlpha - (posX + m_imgFocus.GetWidth() + m_buttonGap)) / (fStartAlpha - m_posX) * m_iAlpha * 2.55f; + } + if (posX >= fEndAlpha) + { + fAlpha -= (posX - fEndAlpha) / (m_posX + m_width - fEndAlpha) * m_iAlpha * 2.55f; + fAlpha1 -= ((posX + m_imgFocus.GetWidth() + m_buttonGap) - fEndAlpha) / (m_posX + m_width - fEndAlpha) * m_iAlpha * 2.55f; + } + if (fAlpha < 0) fAlpha = 0; + if (fAlpha1 < 0) fAlpha1 = 0; + if (fAlpha > 255) fAlpha = 255.0f; + if (fAlpha1 > 255) fAlpha1 = 255.0f; + pImage->SetAlpha((unsigned char)(fAlpha + 0.5f)); + } + else + { + if (posY < fStartAlpha) + { + fAlpha -= (fStartAlpha - posY) / (fStartAlpha - m_posY) * m_iAlpha * 2.55f; + fAlpha1 -= (fStartAlpha - (posY + m_imgFocus.GetHeight() + m_buttonGap)) / (fStartAlpha - m_posY) * m_iAlpha * 2.55f; + } + if (posY > fEndAlpha) + { + fAlpha -= (posY - fEndAlpha) / (m_posY + m_height - fEndAlpha) * m_iAlpha * 2.55f; + fAlpha1 -= ((posY + m_imgFocus.GetHeight() + m_buttonGap) - fEndAlpha) / (m_posY + m_height - fEndAlpha) * m_iAlpha * 2.55f; + } + if (fAlpha < 0) fAlpha = 0; + if (fAlpha1 < 0) fAlpha1 = 0; + if (fAlpha > 255) fAlpha = 255.0f; + if (fAlpha1 > 255) fAlpha1 = 255.0f; + pImage->SetAlpha((unsigned char)(fAlpha + 0.5f)); + } + pImage->SetPosition(posX, posY); + pImage->SetWidth(m_imgNoFocus.GetWidth()); + pImage->SetHeight(m_imgNoFocus.GetHeight()); + pImage->Render(); + } + iOffset = GetNext(iOffset); + if (m_bHorizontal) + posX += m_imgFocus.GetWidth() + m_buttonGap; + else + posY += m_imgFocus.GetHeight() + m_buttonGap; +} + +int CGUIButtonScroller::GetActiveButtonID() const +{ + int iButton = GetActiveButton(); + if (iButton < 0 || iButton >= (int)m_vecButtons.size()) return 0; + return m_vecButtons[iButton]->id; +} + +int CGUIButtonScroller::GetActiveButton() const +{ + if (m_iCurrentSlot < 0) return -1; + int iCurrentItem = m_iOffset; + for (int i = 0; i < m_iCurrentSlot; i++) + if (GetNext(iCurrentItem) != -1) iCurrentItem = GetNext(iCurrentItem); + return iCurrentItem; +} + +void CGUIButtonScroller::GetScrollZone(float &fStartAlpha, float &fEndAlpha) +{ + // check if we are in the scrollable zone (alpha fade area) + // calculate our alpha amount + int iMinSlot = m_iDefaultSlot - m_iMovementRange; + if (iMinSlot < 0) iMinSlot = 0; + int iMaxSlot = m_iDefaultSlot + m_iMovementRange + 1; + if (iMaxSlot > m_iNumSlots) iMaxSlot = m_iNumSlots; + // calculate the amount of pixels between 0 and iMinSlot + if (m_bHorizontal) + { + fStartAlpha = m_posX + iMinSlot * (m_imgFocus.GetWidth() + m_buttonGap); + fEndAlpha = m_posX + iMaxSlot * (m_imgFocus.GetWidth() + m_buttonGap) - m_buttonGap; + } + else + { + fStartAlpha = m_posY + iMinSlot * (m_imgFocus.GetHeight() + m_buttonGap); + fEndAlpha = m_posY + iMaxSlot * (m_imgFocus.GetHeight() + m_buttonGap) - m_buttonGap; + } +} + +bool CGUIButtonScroller::OnMouseOver(const CPoint &point) +{ + float fStartAlpha, fEndAlpha; + GetScrollZone(fStartAlpha, fEndAlpha); + if (m_bHorizontal) + { + if (point.x < fStartAlpha) // scroll down + { + m_bScrollUp = false; + if (m_iSlowScrollCount > 10) m_iSlowScrollCount = 0; + if (m_bSmoothScrolling || m_iSlowScrollCount == 0) + m_bScrollDown = true; + else + m_bScrollDown = false; + m_iSlowScrollCount++; + m_fScrollSpeed = 50.0f + SCROLL_SPEED - (point.x - fStartAlpha) / (m_posX - fStartAlpha) * 50.0f; + } + else if (point.x > fEndAlpha - 1) // scroll up + { + m_bScrollDown = false; + if (m_iSlowScrollCount > 10) m_iSlowScrollCount = 0; + if (m_bSmoothScrolling || m_iSlowScrollCount == 0) + m_bScrollUp = true; + else + m_bScrollUp = false; + m_fScrollSpeed = 50.0f + SCROLL_SPEED - (point.x - fEndAlpha) / (m_posX + m_width - fEndAlpha) * 50.0f; + } + else // call base class + { // select the appropriate item, and call the base class (to set focus) + m_iCurrentSlot = (int)((point.x - m_posX) / (m_imgFocus.GetWidth() + m_buttonGap)); + } + } + else + { + if (point.y < fStartAlpha) // scroll down + { + m_bScrollUp = false; + if (m_iSlowScrollCount > 10) m_iSlowScrollCount = 0; + if (m_bSmoothScrolling || m_iSlowScrollCount == 0) + m_bScrollDown = true; + else + m_bScrollDown = false; + m_iSlowScrollCount++; + m_fScrollSpeed = 50.0f + SCROLL_SPEED - (point.y - fStartAlpha) / (m_posY - fStartAlpha) * 50.0f; + } + else if (point.y > fEndAlpha - 1) // scroll up + { + m_bScrollDown = false; + if (m_iSlowScrollCount > 10) m_iSlowScrollCount = 0; + if (m_bSmoothScrolling || m_iSlowScrollCount == 0) + m_bScrollUp = true; + else + m_bScrollUp = false; + m_iSlowScrollCount++; m_fScrollSpeed = 50.0f + SCROLL_SPEED - (point.y - fEndAlpha) / (m_posY + m_height - fEndAlpha) * 50.0f; + } + else + { // select the appropriate item, and call the base class (to set focus) + m_iCurrentSlot = (int)((point.y - m_posY) / (m_imgFocus.GetHeight() + m_buttonGap)); + } + } + return CGUIControl::OnMouseOver(point); +} + +bool CGUIButtonScroller::OnMouseClick(int button, const CPoint &point) +{ + if (button != MOUSE_LEFT_BUTTON && button != MOUSE_RIGHT_BUTTON) return false; + // check if we are in the clickable button zone + float fStartAlpha, fEndAlpha; + GetScrollZone(fStartAlpha, fEndAlpha); + if (m_bHorizontal) + { + if (point.x >= fStartAlpha && point.x <= fEndAlpha) + { // click the appropriate item + m_iCurrentSlot = (int)((point.x - m_posX) / (m_imgFocus.GetWidth() + m_buttonGap)); + CAction action; + if (button == MOUSE_LEFT_BUTTON) + action.id = ACTION_SELECT_ITEM; + if (button == MOUSE_RIGHT_BUTTON) + action.id = ACTION_CONTEXT_MENU; + OnAction(action); + return true; + } + } + else + { + if (point.y >= fStartAlpha && point.y <= fEndAlpha) + { + m_iCurrentSlot = (int)((point.y - m_posY) / (m_imgFocus.GetHeight() + m_buttonGap)); + CAction action; + if (button == MOUSE_LEFT_BUTTON) + action.id = ACTION_SELECT_ITEM; + if (button == MOUSE_RIGHT_BUTTON) + action.id = ACTION_CONTEXT_MENU; + OnAction(action); + return true; + } + } + return false; +} + +bool CGUIButtonScroller::OnMouseWheel(char wheel, const CPoint &point) +{ + // check if we are within the clickable button zone + float fStartAlpha, fEndAlpha; + GetScrollZone(fStartAlpha, fEndAlpha); + if ((m_bHorizontal && point.x >= fStartAlpha && point.x <= fEndAlpha) || + (!m_bHorizontal && point.y >= fStartAlpha && point.y <= fEndAlpha)) + { + if (wheel > 0) + m_bScrollDown = true; + else + m_bScrollUp = true; + m_fScrollSpeed = SCROLL_SPEED; + return true; + } + return false; +} + +CStdString CGUIButtonScroller::GetDescription() const +{ + if (GetActiveButton() >= 0) + { + CStdString strLabel = m_vecButtons[GetActiveButton()]->strLabel; + return strLabel; + } + return ""; +} + +void CGUIButtonScroller::SaveStates(vector<CControlState> &states) +{ + states.push_back(CControlState(GetID(), GetActiveButton())); +} diff --git a/guilib/GUIButtonScroller.h b/guilib/GUIButtonScroller.h new file mode 100644 index 0000000000..76d4e5f34a --- /dev/null +++ b/guilib/GUIButtonScroller.h @@ -0,0 +1,128 @@ +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControl.h" +#include "GUITexture.h" +#include "GUIActionDescriptor.h" + +class TiXmlNode; + +class CButton +{ +public: + CButton() + { + id = 0; + info = 0; + imageFocus = imageNoFocus = NULL; + }; + ~CButton() + { + delete imageFocus; + delete imageNoFocus; + } + int id; + int info; + std::string strLabel; + std::vector<CGUIActionDescriptor> clickActions; + CGUITexture *imageFocus; + CGUITexture *imageNoFocus; +}; + +class CGUIButtonScroller : + public CGUIControl +{ +public: + CGUIButtonScroller(int parentID, int controlID, float posX, float posY, float width, float height, float gap, int iSlots, int iDefaultSlot, int iMovementRange, bool bHorizontal, int iAlpha, bool bWrapAround, bool bSmoothScrolling, const CTextureInfo& textureFocus, const CTextureInfo& textureNoFocus, const CLabelInfo& labelInfo); + CGUIButtonScroller(const CGUIButtonScroller &from); + virtual ~CGUIButtonScroller(void); + virtual CGUIButtonScroller *Clone() const { return new CGUIButtonScroller(*this); }; + + virtual bool OnAction(const CAction &action); + virtual bool OnMessage(CGUIMessage &message); + virtual void OnUp(); + virtual void OnLeft(); + virtual void OnRight(); + virtual void OnDown(); + virtual bool OnMouseOver(const CPoint &point); + virtual bool OnMouseClick(int button, const CPoint &point); + virtual bool OnMouseWheel(char wheel, const CPoint &point); + virtual void Render(); + virtual void AllocResources(); + virtual void FreeResources(); + virtual void DynamicResourceAlloc(bool bOnOff); + void ClearButtons(); + void AddButton(const std::string &strLabel, const CStdString &strExecute, const int iIcon); + void SetActiveButton(int iButton); + int GetActiveButton() const; + int GetActiveButtonID() const; + virtual CStdString GetDescription() const; + virtual void SaveStates(std::vector<CControlState> &states); + void LoadButtons(TiXmlNode *node); + +protected: + virtual void UpdateColors(); + int GetNext(int iCurrent) const; + int GetPrevious(int iCurrent); + int GetButton(int iOffset); + void DoUp(); + void DoDown(); + void RenderItem(float &posX, float &posY, int &iOffset, bool bText); + void GetScrollZone(float &fStartAlpha, float &fEndAlpha); +private: + // saved variables from the xml (as this control is user editable...) + int m_iXMLNumSlots; + int m_iXMLDefaultSlot; + float m_xmlPosX; + float m_xmlPosY; + float m_xmlWidth; + float m_xmlHeight; + + float m_buttonGap; // gap between buttons + int m_iNumSlots; // number of button slots available + int m_iDefaultSlot; // default highlight position + int m_iMovementRange; // amoung that we can move the highlight + bool m_bHorizontal; // true if it's a horizontal button bar + int m_iAlpha; // amount of alpha (0..100) + bool m_bWrapAround; // whether the buttons wrap around or not + bool m_bSmoothScrolling; // whether there is smooth scrolling or not + + int m_iCurrentSlot; // currently highlighted slot + + int m_iOffset; // first item in the list + float m_scrollOffset; // offset when scrolling + bool m_bScrollUp; // true if we're scrolling up (or left) + bool m_bScrollDown; // true if scrolling down (or right) + bool m_bMoveUp; // true if we're scrolling up (or left) + bool m_bMoveDown; // true if scrolling down (or right) + float m_fScrollSpeed; // speed of scrolling + float m_fAnalogScrollSpeed; // speed of analog scroll (triggers) + // stuff we need for the buttons... + std::vector<CButton*> m_vecButtons; + typedef std::vector<CButton*>::iterator ivecButtons; + CGUITexture m_imgFocus; + CGUITexture m_imgNoFocus; + + CLabelInfo m_label; + int m_iSlowScrollCount; +}; diff --git a/guilib/GUICallback.h b/guilib/GUICallback.h new file mode 100644 index 0000000000..9e2deb5767 --- /dev/null +++ b/guilib/GUICallback.h @@ -0,0 +1,193 @@ +#if !defined(GUICALLBACK_H) +#define GUICALLBACK_H + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "assert.h" +#include "memory.h" + +#ifndef __GNUC__ +#pragma warning( disable : 4786 ) +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////// +// NOTE FROM RUNTiME: An Event holds a weak reference to class method, it acts similar to a // +// C# delegate method E.g. instantiate an Event and assign it an EventHandler, whenever the // +// Event is fired the EventHandler is executed. // +// - There is a 2nd variant of Event called a Callback, this only differs in that a Callback // +// can return a value after the CallbackHandler has been executed. // +// - There is a nasty memcpy that gets us over the compiler restriction that prevents us from // +// casting between two different method pointers, not the cleanest solution granted, but it's // +// the only way I know how to get around this limitation. // +//////////////////////////////////////////////////////////////////////////////////////////////// + +template <class Cookie> +class GUIEvent +{ +public: + + typedef void (GUIEvent::*MethodPtr)(Cookie); + + GUIEvent() + { + m_pInstance = NULL; + m_pMethod = NULL; + } + + // Assign an EventHandler (EventHandler's are derived from Event) + GUIEvent<Cookie> &operator=(GUIEvent<Cookie> &aEvent) + { + if (&aEvent) + { + m_pInstance = aEvent.m_pInstance; + m_pMethod = aEvent.m_pMethod; + } + else + { + GUIEvent(); + } + + return *this; + } + + // Are the class instance and method pointers initialised? + bool HasAHandler() + { + return (m_pInstance && m_pMethod); + } + + // Execute the associated class method + void Fire(Cookie aCookie) + { + if (HasAHandler()) + { + (m_pInstance->*m_pMethod)(aCookie); + } + else + { + // Event is uninitialized, no handler has been assigned + assert(0); + } + } + MethodPtr m_pMethod; + +protected: + GUIEvent* m_pInstance; +}; + +template <class Class, class Cookie> +class GUIEventHandler : public GUIEvent<Cookie> +{ +public: + typedef void (Class::*MethodPtr)(Cookie); + + GUIEventHandler(Class* pInstance, MethodPtr aMethodPtr) + { + GUIEvent<Cookie>::m_pInstance = (GUIEvent<Cookie>*) ((LPVOID) pInstance); + +#ifndef _LINUX + // Its dirty but it works! + memcpy(&m_pMethod, &aMethodPtr, sizeof(GUIEvent<Cookie>::m_pMethod)); +#else + // Well, GCC doesn't like that dirty stuff... here's another version of the same thing + // but even dirtier *grin* + +#define my_offsetof(TYPE, MEMBER) \ + ((size_t)((char *)&(((TYPE *)0x10)->MEMBER) - (char*)0x10)) + + void* target = (void*) (((char*) this) + my_offsetof(GUIEvent<Cookie>, m_pMethod)); + memcpy(target, &aMethodPtr, sizeof(GUIEvent<Cookie>::m_pMethod)); +#endif + } +}; + + +// Callbacks are identical to Events except that a Callback returns a result value +template <class Result, class Cookie> +class Callback +{ +public: + typedef Result (Callback::*MethodPtr)(Cookie); + + Callback() + { + m_pInstance = NULL; + m_pMethod = NULL; + } + + // Assign a CallbackHandler (CallbackHandler's are derived from Callback) + Callback<Result, Cookie> &operator=(Callback<Result, Cookie> &aCallback) + { + if (&aCallback) + { + m_pInstance = aCallback.m_pInstance; + m_pMethod = aCallback.m_pMethod; + } + else + { + Callback(); + } + + return *this; + } + + // Are the class instance and method pointers initialised? + bool HasAHandler() + { + return (m_pInstance && m_pMethod); + } + + // Execute the associated class method and return the result + Result Fire(Cookie aCookie) + { + if (HasAHandler()) + { + return (m_pInstance->*m_pMethod)(aCookie); + } + + // Callback is uninitialized, no handler has been assigned + assert(0); + return 0; + } + +protected: + Callback* m_pInstance; + MethodPtr m_pMethod; +}; + + +template <class Class, class Result, class Cookie> +class CallbackHandler : public Callback<Result, Cookie> +{ +public: + typedef Result (Class::*MethodPtr)(Cookie); + + CallbackHandler (Class* pInstance, MethodPtr aMethodPtr) + { + Callback<Result, Cookie>::m_pInstance = (Callback<Result, Cookie>*) ((LPVOID) pInstance); + // Its dirty but it works! + memcpy(&Callback<Result, Cookie>::m_pMethod, &aMethodPtr, sizeof(Callback<Result, Cookie>::m_pMethod)); + } +}; + +#endif // GUICALLBACK_H + diff --git a/guilib/GUICheckMarkControl.cpp b/guilib/GUICheckMarkControl.cpp new file mode 100644 index 0000000000..4a081576f2 --- /dev/null +++ b/guilib/GUICheckMarkControl.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUICheckMarkControl.h" +#include "utils/CharsetConverter.h" +#include "GUIFontManager.h" +#include "MouseStat.h" +#include "Key.h" + +using namespace std; + +CGUICheckMarkControl::CGUICheckMarkControl(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& textureCheckMark, const CTextureInfo& textureCheckMarkNF, float checkWidth, float checkHeight, const CLabelInfo &labelInfo) + : CGUIControl(parentID, controlID, posX, posY, width, height) + , m_imgCheckMark(posX, posY, checkWidth, checkHeight, textureCheckMark) + , m_imgCheckMarkNoFocus(posX, posY, checkWidth, checkHeight, textureCheckMarkNF) + , m_textLayout(labelInfo.font, false) +{ + m_strLabel = ""; + m_label = labelInfo; + m_bSelected = false; + ControlType = GUICONTROL_CHECKMARK; +} + +CGUICheckMarkControl::~CGUICheckMarkControl(void) +{} + +void CGUICheckMarkControl::Render() +{ + m_textLayout.Update(m_strLabel); + + float fTextHeight, fTextWidth; + m_textLayout.GetTextExtent(fTextWidth, fTextHeight); + m_width = fTextWidth + 5 + m_imgCheckMark.GetWidth(); + m_height = m_imgCheckMark.GetHeight(); + + float textPosX = m_posX; + float textPosY = m_posY + m_height * 0.5f; + float checkMarkPosX = m_posX; + + if (m_label.align & (XBFONT_RIGHT | XBFONT_CENTER_X)) + textPosX += m_imgCheckMark.GetWidth() + 5; + else + checkMarkPosX += fTextWidth + 5; + + if (IsDisabled() ) + m_textLayout.Render(textPosX, textPosY, 0, m_label.disabledColor, m_label.shadowColor, XBFONT_CENTER_Y, 0, true); + else if (HasFocus() && m_label.focusedColor) + m_textLayout.Render(textPosX, textPosY, 0, m_label.focusedColor, m_label.shadowColor, XBFONT_CENTER_Y, 0); + else + m_textLayout.Render(textPosX, textPosY, 0, m_label.textColor, m_label.shadowColor, XBFONT_CENTER_Y, 0); + + if (m_bSelected) + { + m_imgCheckMark.SetPosition(checkMarkPosX, m_posY); + m_imgCheckMark.Render(); + } + else + { + m_imgCheckMarkNoFocus.SetPosition(checkMarkPosX, m_posY); + m_imgCheckMarkNoFocus.Render(); + } + CGUIControl::Render(); +} + +bool CGUICheckMarkControl::OnAction(const CAction &action) +{ + if (action.id == ACTION_SELECT_ITEM) + { + m_bSelected = !m_bSelected; + CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID(), action.id); + SendWindowMessage(msg); + return true; + } + return CGUIControl::OnAction(action); +} + +bool CGUICheckMarkControl::OnMessage(CGUIMessage& message) +{ + if ( message.GetControlId() == GetID() ) + { + if (message.GetMessage() == GUI_MSG_LABEL_SET) + { + m_strLabel = message.GetLabel(); + return true; + } + } + if (CGUIControl::OnMessage(message)) return true; + return false; +} + +void CGUICheckMarkControl::AllocResources() +{ + CGUIControl::AllocResources(); + m_imgCheckMark.AllocResources(); + m_imgCheckMarkNoFocus.AllocResources(); +} + +void CGUICheckMarkControl::FreeResources() +{ + CGUIControl::FreeResources(); + m_imgCheckMark.FreeResources(); + m_imgCheckMarkNoFocus.FreeResources(); +} + +void CGUICheckMarkControl::DynamicResourceAlloc(bool bOnOff) +{ + CGUIControl::DynamicResourceAlloc(bOnOff); + m_imgCheckMark.DynamicResourceAlloc(bOnOff); + m_imgCheckMarkNoFocus.DynamicResourceAlloc(bOnOff); +} + +void CGUICheckMarkControl::SetSelected(bool bOnOff) +{ + m_bSelected = bOnOff; +} + +bool CGUICheckMarkControl::GetSelected() const +{ + return m_bSelected; +} + +bool CGUICheckMarkControl::OnMouseClick(int button, const CPoint &point) +{ + if (button != MOUSE_LEFT_BUTTON) return false; + g_Mouse.SetState(MOUSE_STATE_CLICK); + CAction action; + action.id = ACTION_SELECT_ITEM; + OnAction(action); + return true; +} + +void CGUICheckMarkControl::SetLabel(const string &label) +{ + m_strLabel = label; +} + +void CGUICheckMarkControl::PythonSetLabel(const CStdString &strFont, const string &strText, color_t textColor) +{ + m_label.font = g_fontManager.GetFont(strFont); + m_label.textColor = textColor; + m_label.focusedColor = textColor; + m_strLabel = strText; +} + +void CGUICheckMarkControl::PythonSetDisabledColor(color_t disabledColor) +{ + m_label.disabledColor = disabledColor; +} + +void CGUICheckMarkControl::UpdateColors() +{ + m_label.UpdateColors(); + CGUIControl::UpdateColors(); + m_imgCheckMark.SetDiffuseColor(m_diffuseColor); + m_imgCheckMarkNoFocus.SetDiffuseColor(m_diffuseColor); +} diff --git a/guilib/GUICheckMarkControl.h b/guilib/GUICheckMarkControl.h new file mode 100644 index 0000000000..5ff86051d9 --- /dev/null +++ b/guilib/GUICheckMarkControl.h @@ -0,0 +1,74 @@ +/*! +\file GUICheckMarkControl.h +\brief +*/ + +#ifndef CGUILIB_GUICHECKMARK_CONTROL_H +#define CGUILIB_GUICHECKMARK_CONTROL_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUITexture.h" +#include "GUITextLayout.h" +#include "GUIControl.h" + +/*! + \ingroup controls + \brief + */ +class CGUICheckMarkControl: public CGUIControl +{ +public: + CGUICheckMarkControl(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& textureCheckMark, const CTextureInfo& textureCheckMarkNF, float checkWidth, float checkHeight, const CLabelInfo &labelInfo); + virtual ~CGUICheckMarkControl(void); + virtual CGUICheckMarkControl *Clone() const { return new CGUICheckMarkControl(*this); }; + + virtual void Render(); + virtual bool OnAction(const CAction &action) ; + virtual bool OnMessage(CGUIMessage& message); + virtual void AllocResources(); + virtual void FreeResources(); + virtual void DynamicResourceAlloc(bool bOnOff); + + void SetLabel(const std::string& strLabel); + const std::string GetLabel() const { return m_strLabel; }; + const CLabelInfo& GetLabelInfo() const { return m_label; }; + void SetSelected(bool bOnOff); + bool GetSelected() const; + bool OnMouseClick(int button, const CPoint &point); + + void PythonSetLabel(const CStdString &strFont, const std::string &strText, color_t textColor); + void PythonSetDisabledColor(color_t disabledColor); + +protected: + virtual void UpdateColors(); + CGUITexture m_imgCheckMark; + CGUITexture m_imgCheckMarkNoFocus; + + CLabelInfo m_label; + CGUITextLayout m_textLayout; + std::string m_strLabel; + bool m_bSelected; +}; +#endif diff --git a/guilib/GUIColorManager.cpp b/guilib/GUIColorManager.cpp new file mode 100644 index 0000000000..e8394db8ee --- /dev/null +++ b/guilib/GUIColorManager.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIColorManager.h" +#include "Util.h" +#include "FileSystem/SpecialProtocol.h" +#include "SkinInfo.h" +#include "utils/log.h" +#include "tinyXML/tinyxml.h" + +CGUIColorManager g_colorManager; + +CGUIColorManager::CGUIColorManager(void) +{ +} + +CGUIColorManager::~CGUIColorManager(void) +{ + Clear(); +} + +void CGUIColorManager::Clear() +{ + m_colors.clear(); +} + +// load the color file in +void CGUIColorManager::Load(const CStdString &colorFile) +{ + Clear(); + + // first load the default color map if it exists + CStdString path, basePath; + CUtil::AddFileToFolder(g_SkinInfo.GetBaseDir(), "colors", basePath); + CUtil::AddFileToFolder(basePath, "defaults.xml", path); + + TiXmlDocument xmlDoc; + if (xmlDoc.LoadFile(PTH_IC(path))) + LoadXML(xmlDoc); + + // now the color map requested + if (colorFile.CompareNoCase("SKINDEFAULT") == 0) + return; // nothing to do + + CUtil::AddFileToFolder(basePath, colorFile, path); + CLog::Log(LOGINFO, "Loading colors from %s", path.c_str()); + + if (xmlDoc.LoadFile(path)) + LoadXML(xmlDoc); +} + +bool CGUIColorManager::LoadXML(TiXmlDocument &xmlDoc) +{ + TiXmlElement* pRootElement = xmlDoc.RootElement(); + + CStdString strValue = pRootElement->Value(); + if (strValue != CStdString("colors")) + { + CLog::Log(LOGERROR, "color file doesnt start with <colors>"); + return false; + } + + const TiXmlElement *color = pRootElement->FirstChildElement("color"); + + while (color) + { + if (color->FirstChild() && color->Attribute("name")) + { + color_t value = 0xffffffff; + sscanf(color->FirstChild()->Value(), "%x", (unsigned int*) &value); + CStdString name = color->Attribute("name"); + iColor it = m_colors.find(name); + if (it != m_colors.end()) + (*it).second = value; + else + m_colors.insert(make_pair(name, value)); + } + color = color->NextSiblingElement("color"); + } + return true; +} + +// lookup a color and return it's hex value +color_t CGUIColorManager::GetColor(const CStdString &color) const +{ + // look in our color map + CStdString trimmed(color); + trimmed.TrimLeft("= "); + icColor it = m_colors.find(trimmed); + if (it != m_colors.end()) + return (*it).second; + + // try converting hex directly + color_t value = 0; + sscanf(trimmed.c_str(), "%x", &value); + return value; +} + diff --git a/guilib/GUIColorManager.h b/guilib/GUIColorManager.h new file mode 100644 index 0000000000..e1f66e82c6 --- /dev/null +++ b/guilib/GUIColorManager.h @@ -0,0 +1,70 @@ +/*! +\file GUIColorManager.h +\brief +*/ + +#ifndef GUILIB_COLORMANAGER_H +#define GUILIB_COLORMANAGER_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +/*! + \ingroup textures + \brief + */ + +#include "StdString.h" + +#include <map> + +class TiXmlDocument; + +typedef uint32_t color_t; + +class CGUIColorManager +{ +public: + CGUIColorManager(void); + virtual ~CGUIColorManager(void); + + void Load(const CStdString &colorFile); + + color_t GetColor(const CStdString &color) const; + + void Clear(); + +protected: + bool LoadXML(TiXmlDocument &xmlDoc); + + std::map<CStdString, color_t> m_colors; + typedef std::map<CStdString, color_t>::iterator iColor; + typedef std::map<CStdString, color_t>::const_iterator icColor; +}; + +/*! + \ingroup textures + \brief + */ +extern CGUIColorManager g_colorManager; +#endif diff --git a/guilib/GUIControl.cpp b/guilib/GUIControl.cpp new file mode 100644 index 0000000000..d7ffc3caae --- /dev/null +++ b/guilib/GUIControl.cpp @@ -0,0 +1,887 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControl.h" + +#include "utils/GUIInfoManager.h" +#include "utils/log.h" +#include "LocalizeStrings.h" +#include "GUIWindowManager.h" +#include "GUIControlProfiler.h" +#include "MouseStat.h" +#include "Key.h" + +using namespace std; + +CGUIControl::CGUIControl() +{ + m_hasRendered = false; + m_bHasFocus = false; + m_controlID = 0; + m_parentID = 0; + m_visible = VISIBLE; + m_visibleFromSkinCondition = true; + m_forceHidden = false; + m_visibleCondition = 0; + m_enableCondition = 0; + m_enabled = true; + m_diffuseColor = 0xffffffff; + m_posX = 0; + m_posY = 0; + m_width = 0; + m_height = 0; + m_controlLeft = 0; + m_controlRight = 0; + m_controlUp = 0; + m_controlDown = 0; + m_controlNext = 0; + m_controlPrev = 0; + ControlType = GUICONTROL_UNKNOWN; + m_bInvalidated = true; + m_bAllocated=false; + m_parentControl = NULL; + m_hasCamera = false; + m_pushedUpdates = false; + m_pulseOnSelect = false; +} + +CGUIControl::CGUIControl(int parentID, int controlID, float posX, float posY, float width, float height) +: m_hitRect(posX, posY, posX + width, posY + height) +{ + m_posX = posX; + m_posY = posY; + m_width = width; + m_height = height; + m_bHasFocus = false; + m_controlID = controlID; + m_parentID = parentID; + m_visible = VISIBLE; + m_visibleFromSkinCondition = true; + m_diffuseColor = 0xffffffff; + m_forceHidden = false; + m_visibleCondition = 0; + m_enableCondition = 0; + m_enabled = true; + m_controlLeft = 0; + m_controlRight = 0; + m_controlUp = 0; + m_controlDown = 0; + m_controlNext = 0; + m_controlPrev = 0; + ControlType = GUICONTROL_UNKNOWN; + m_bInvalidated = true; + m_bAllocated=false; + m_hasRendered = false; + m_parentControl = NULL; + m_hasCamera = false; + m_pushedUpdates = false; + m_pulseOnSelect = false; +} + + +CGUIControl::~CGUIControl(void) +{ + +} + +void CGUIControl::AllocResources() +{ + m_hasRendered = false; + m_bInvalidated = true; + m_bAllocated=true; +} + +void CGUIControl::FreeResources() +{ + if (m_bAllocated) + { + // Reset our animation states - not conditional anims though. + // I'm not sure if this is needed for most cases anyway. I believe it's only here + // because some windows aren't loaded on demand + for (unsigned int i = 0; i < m_animations.size(); i++) + { + CAnimation &anim = m_animations[i]; + if (anim.GetType() != ANIM_TYPE_CONDITIONAL) + anim.ResetAnimation(); + } + m_bAllocated=false; + } + m_hasRendered = false; +} + +void CGUIControl::DynamicResourceAlloc(bool bOnOff) +{ + +} + +// the main render routine. +// 1. animate and set the animation transform +// 2. if visible, paint +// 3. reset the animation transform +void CGUIControl::DoRender(DWORD currentTime) +{ + Animate(currentTime); + if (m_hasCamera) + g_graphicsContext.SetCameraPosition(m_camera); + if (IsVisible()) + { + GUIPROFILER_RENDER_BEGIN(this); + Render(); + GUIPROFILER_RENDER_END(this); + } + if (m_hasCamera) + g_graphicsContext.RestoreCameraPosition(); + g_graphicsContext.RemoveTransform(); +} + +void CGUIControl::Render() +{ + m_bInvalidated = false; + m_hasRendered = true; +} + +bool CGUIControl::OnAction(const CAction &action) +{ + switch (action.id) + { + case ACTION_MOVE_DOWN: + if (!HasFocus()) return false; + OnDown(); + return true; + break; + + case ACTION_MOVE_UP: + if (!HasFocus()) return false; + OnUp(); + return true; + break; + + case ACTION_MOVE_LEFT: + if (!HasFocus()) return false; + OnLeft(); + return true; + break; + + case ACTION_MOVE_RIGHT: + if (!HasFocus()) return false; + OnRight(); + return true; + break; + + case ACTION_NEXT_CONTROL: + if (!HasFocus()) return false; + OnNextControl(); + return true; + break; + + case ACTION_PREV_CONTROL: + if (!HasFocus()) return false; + OnPrevControl(); + return true; + break; + } + return false; +} + +// Movement controls (derived classes can override) +void CGUIControl::OnUp() +{ + if (HasFocus()) + { + if (m_upActions.size()) + ExecuteActions(m_upActions); + else if (m_controlID != m_controlUp) + { + // Send a message to the window with the sender set as the window + CGUIMessage msg(GUI_MSG_MOVE, GetParentID(), GetID(), ACTION_MOVE_UP); + SendWindowMessage(msg); + } + } +} + +void CGUIControl::OnDown() +{ + if (HasFocus()) + { + if (m_downActions.size()) + ExecuteActions(m_downActions); + else if (m_controlID != m_controlDown) + { + // Send a message to the window with the sender set as the window + CGUIMessage msg(GUI_MSG_MOVE, GetParentID(), GetID(), ACTION_MOVE_DOWN); + SendWindowMessage(msg); + } + } +} + +void CGUIControl::OnLeft() +{ + if (HasFocus()) + { + if (m_leftActions.size()) + ExecuteActions(m_leftActions); + else if (m_controlID != m_controlLeft) + { + // Send a message to the window with the sender set as the window + CGUIMessage msg(GUI_MSG_MOVE, GetParentID(), GetID(), ACTION_MOVE_LEFT); + SendWindowMessage(msg); + } + } +} + +void CGUIControl::OnRight() +{ + if (HasFocus()) + { + if (m_rightActions.size()) + ExecuteActions(m_rightActions); + else if (m_controlID != m_controlRight) + { + // Send a message to the window with the sender set as the window + CGUIMessage msg(GUI_MSG_MOVE, GetParentID(), GetID(), ACTION_MOVE_RIGHT); + SendWindowMessage(msg); + } + } +} + +void CGUIControl::OnNextControl() +{ + if (m_controlID != m_controlNext) + { + // Send a message to the window with the sender set as the window + CGUIMessage msg(GUI_MSG_MOVE, GetParentID(), GetID(), ACTION_NEXT_CONTROL, m_controlNext); + SendWindowMessage(msg); + } +} + +void CGUIControl::OnPrevControl() +{ + if (m_controlID != m_controlPrev) + { + // Send a message to the window with the sender set as the window + CGUIMessage msg(GUI_MSG_MOVE, GetParentID(), GetID(), ACTION_PREV_CONTROL, m_controlPrev); + SendWindowMessage(msg); + } +} + +bool CGUIControl::SendWindowMessage(CGUIMessage &message) +{ + CGUIWindow *pWindow = m_gWindowManager.GetWindow(GetParentID()); + if (pWindow) + return pWindow->OnMessage(message); + return g_graphicsContext.SendMessage(message); +} + +int CGUIControl::GetID(void) const +{ + return m_controlID; +} + + +int CGUIControl::GetParentID(void) const +{ + return m_parentID; +} + +bool CGUIControl::HasFocus(void) const +{ + return m_bHasFocus; +} + +void CGUIControl::SetFocus(bool focus) +{ + if (m_bHasFocus && !focus) + QueueAnimation(ANIM_TYPE_UNFOCUS); + else if (!m_bHasFocus && focus) + QueueAnimation(ANIM_TYPE_FOCUS); + m_bHasFocus = focus; +} + +bool CGUIControl::OnMessage(CGUIMessage& message) +{ + if ( message.GetControlId() == GetID() ) + { + switch (message.GetMessage() ) + { + case GUI_MSG_SETFOCUS: + // if control is disabled then move 2 the next control + if ( !CanFocus() ) + { + CLog::Log(LOGERROR, "Control %u in window %u has been asked to focus, " + "but it can't", + GetID(), GetParentID()); + return false; + } + SetFocus(true); + { + // inform our parent window that this has happened + CGUIMessage message(GUI_MSG_FOCUSED, GetParentID(), GetID()); + if (m_parentControl) + m_parentControl->OnMessage(message); + } + return true; + break; + + case GUI_MSG_LOSTFOCUS: + { + SetFocus(false); + // and tell our parent so it can unfocus + if (m_parentControl) + m_parentControl->OnMessage(message); + return true; + } + break; + + case GUI_MSG_VISIBLE: + if (m_visibleCondition) + m_visible = g_infoManager.GetBool(m_visibleCondition, m_parentID) ? VISIBLE : HIDDEN; + else + m_visible = VISIBLE; + m_forceHidden = false; + return true; + break; + + case GUI_MSG_HIDDEN: + m_forceHidden = true; + // reset any visible animations that are in process + if (IsAnimating(ANIM_TYPE_VISIBLE)) + { +// CLog::DebugLog("Resetting visible animation on control %i (we are %s)", m_controlID, m_visible ? "visible" : "hidden"); + CAnimation *visibleAnim = GetAnimation(ANIM_TYPE_VISIBLE); + if (visibleAnim) visibleAnim->ResetAnimation(); + } + return true; + + // Note that the skin <enable> tag will override these messages + case GUI_MSG_ENABLED: + SetEnabled(true); + return true; + + case GUI_MSG_DISABLED: + SetEnabled(false); + return true; + } + } + return false; +} + +bool CGUIControl::CanFocus() const +{ + if (!IsVisible() && !m_allowHiddenFocus) return false; + if (IsDisabled()) return false; + return true; +} + +bool CGUIControl::IsVisible() const +{ + if (m_forceHidden) return false; + return m_visible == VISIBLE; +} + +bool CGUIControl::IsDisabled() const +{ + return !m_enabled; +} + +void CGUIControl::SetEnabled(bool bEnable) +{ + m_enabled = bEnable; +} + +void CGUIControl::SetEnableCondition(int condition) +{ + m_enableCondition = condition; +} + +void CGUIControl::SetPosition(float posX, float posY) +{ + if ((m_posX != posX) || (m_posY != posY)) + { + m_hitRect += CPoint(posX - m_posX, posY - m_posY); + m_posX = posX; + m_posY = posY; + SetInvalid(); + } +} + +void CGUIControl::SetColorDiffuse(const CGUIInfoColor &color) +{ + m_diffuseColor = color; +} + +float CGUIControl::GetXPosition() const +{ + return m_posX; +} + +float CGUIControl::GetYPosition() const +{ + return m_posY; +} + +float CGUIControl::GetWidth() const +{ + return m_width; +} + +float CGUIControl::GetHeight() const +{ + return m_height; +} + +void CGUIControl::SetNavigation(int up, int down, int left, int right) +{ + m_controlUp = up; + m_controlDown = down; + m_controlLeft = left; + m_controlRight = right; +} + +void CGUIControl::SetTabNavigation(int next, int prev) +{ + m_controlNext = next; + m_controlPrev = prev; +} + +void CGUIControl::SetNavigationActions(const vector<CGUIActionDescriptor> &up, const vector<CGUIActionDescriptor> &down, + const vector<CGUIActionDescriptor> &left, const vector<CGUIActionDescriptor> &right) +{ + m_leftActions = left; + m_rightActions = right; + m_upActions = up; + m_downActions = down; +} + +void CGUIControl::SetWidth(float width) +{ + if (m_width != width) + { + m_width = width; + m_hitRect.x2 = m_hitRect.x1 + width; + SetInvalid(); + } +} + +void CGUIControl::SetHeight(float height) +{ + if (m_height != height) + { + m_height = height; + m_hitRect.y2 = m_hitRect.y1 + height; + SetInvalid(); + } +} + +void CGUIControl::SetVisible(bool bVisible) +{ + // just force to hidden if necessary + m_forceHidden = !bVisible; +/* + if (m_visibleCondition) + bVisible = g_infoManager.GetBool(m_visibleCondition, m_parentID); + if (m_bVisible != bVisible) + { + m_visible = bVisible; + m_visibleFromSkinCondition = bVisible; + SetInvalid(); + }*/ +} + +bool CGUIControl::HitTest(const CPoint &point) const +{ + return m_hitRect.PtInRect(point); +} + +// override this function to implement custom mouse behaviour +bool CGUIControl::OnMouseOver(const CPoint &point) +{ + if (g_Mouse.GetState() != MOUSE_STATE_DRAG) + g_Mouse.SetState(MOUSE_STATE_FOCUS); + if (!CanFocus()) return false; + CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), GetID()); + OnMessage(msg); + return true; +} + +void CGUIControl::UpdateVisibility(const CGUIListItem *item) +{ + if (m_visibleCondition) + { + bool bWasVisible = m_visibleFromSkinCondition; + m_visibleFromSkinCondition = g_infoManager.GetBool(m_visibleCondition, m_parentID, item); + if (!bWasVisible && m_visibleFromSkinCondition) + { // automatic change of visibility - queue the in effect + // CLog::DebugLog("Visibility changed to visible for control id %i", m_controlID); + QueueAnimation(ANIM_TYPE_VISIBLE); + } + else if (bWasVisible && !m_visibleFromSkinCondition) + { // automatic change of visibility - do the out effect + // CLog::DebugLog("Visibility changed to hidden for control id %i", m_controlID); + QueueAnimation(ANIM_TYPE_HIDDEN); + } + } + // check for conditional animations + for (unsigned int i = 0; i < m_animations.size(); i++) + { + CAnimation &anim = m_animations[i]; + if (anim.GetType() == ANIM_TYPE_CONDITIONAL) + anim.UpdateCondition(GetParentID(), item); + } + // and check for conditional enabling - note this overrides SetEnabled() from the code currently + // this may need to be reviewed at a later date + if (m_enableCondition) + m_enabled = g_infoManager.GetBool(m_enableCondition, m_parentID, item); + m_allowHiddenFocus.Update(m_parentID, item); + UpdateColors(); + // and finally, update our control information (if not pushed) + if (!m_pushedUpdates) + UpdateInfo(item); +} + +void CGUIControl::UpdateColors() +{ + m_diffuseColor.Update(); +} + +void CGUIControl::SetInitialVisibility() +{ + if (m_visibleCondition) + { + m_visibleFromSkinCondition = g_infoManager.GetBool(m_visibleCondition, m_parentID); + m_visible = m_visibleFromSkinCondition ? VISIBLE : HIDDEN; + // CLog::DebugLog("Set initial visibility for control %i: %s", m_controlID, m_visible == VISIBLE ? "visible" : "hidden"); + // no need to enquire every frame if we are always visible or always hidden + if (m_visibleCondition == SYSTEM_ALWAYS_TRUE || m_visibleCondition == SYSTEM_ALWAYS_FALSE) + m_visibleCondition = 0; + } + // and handle animation conditions as well + for (unsigned int i = 0; i < m_animations.size(); i++) + { + CAnimation &anim = m_animations[i]; + if (anim.GetType() == ANIM_TYPE_CONDITIONAL) + anim.SetInitialCondition(GetParentID()); + } + m_allowHiddenFocus.Update(m_parentID); + UpdateColors(); +} + +void CGUIControl::SetVisibleCondition(int visible, const CGUIInfoBool &allowHiddenFocus) +{ + m_visibleCondition = visible; + m_allowHiddenFocus = allowHiddenFocus; +} + +void CGUIControl::SetAnimations(const vector<CAnimation> &animations) +{ + m_animations = animations; +} + +void CGUIControl::ResetAnimation(ANIMATION_TYPE type) +{ + for (unsigned int i = 0; i < m_animations.size(); i++) + { + if (m_animations[i].GetType() == type) + m_animations[i].ResetAnimation(); + } +} + +void CGUIControl::ResetAnimations() +{ + for (unsigned int i = 0; i < m_animations.size(); i++) + m_animations[i].ResetAnimation(); +} + +bool CGUIControl::CheckAnimation(ANIMATION_TYPE animType) +{ + // rule out the animations we shouldn't perform + if (!IsVisible() || !HasRendered()) + { // hidden or never rendered - don't allow exit or entry animations for this control + if (animType == ANIM_TYPE_WINDOW_CLOSE) + { // could be animating a (delayed) window open anim, so reset it + ResetAnimation(ANIM_TYPE_WINDOW_OPEN); + return false; + } + } + if (!IsVisible()) + { // hidden - only allow hidden anims if we're animating a visible anim + if (animType == ANIM_TYPE_HIDDEN && !IsAnimating(ANIM_TYPE_VISIBLE)) + { + // update states to force it hidden + UpdateStates(animType, ANIM_PROCESS_NORMAL, ANIM_STATE_APPLIED); + return false; + } + if (animType == ANIM_TYPE_WINDOW_OPEN) + return false; + } + return true; +} + +void CGUIControl::QueueAnimation(ANIMATION_TYPE animType) +{ + if (!CheckAnimation(animType)) + return; + CAnimation *reverseAnim = GetAnimation((ANIMATION_TYPE)-animType, false); + CAnimation *forwardAnim = GetAnimation(animType); + // we first check whether the reverse animation is in progress (and reverse it) + // then we check for the normal animation, and queue it + if (reverseAnim && reverseAnim->IsReversible() && (reverseAnim->GetState() == ANIM_STATE_IN_PROCESS || reverseAnim->GetState() == ANIM_STATE_DELAYED)) + { + reverseAnim->QueueAnimation(ANIM_PROCESS_REVERSE); + if (forwardAnim) forwardAnim->ResetAnimation(); + } + else if (forwardAnim) + { + forwardAnim->QueueAnimation(ANIM_PROCESS_NORMAL); + if (reverseAnim) reverseAnim->ResetAnimation(); + } + else + { // hidden and visible animations delay the change of state. If there is no animations + // to perform, then we should just change the state straightaway + if (reverseAnim) reverseAnim->ResetAnimation(); + UpdateStates(animType, ANIM_PROCESS_NORMAL, ANIM_STATE_APPLIED); + } +} + +CAnimation *CGUIControl::GetAnimation(ANIMATION_TYPE type, bool checkConditions /* = true */) +{ + for (unsigned int i = 0; i < m_animations.size(); i++) + { + CAnimation &anim = m_animations[i]; + if (anim.GetType() == type) + { + if (!checkConditions || !anim.GetCondition() || g_infoManager.GetBool(anim.GetCondition())) + return &anim; + } + } + return NULL; +} + +bool CGUIControl::HasAnimation(ANIMATION_TYPE type) +{ + return (NULL != GetAnimation(type, true)); +} + +void CGUIControl::UpdateStates(ANIMATION_TYPE type, ANIMATION_PROCESS currentProcess, ANIMATION_STATE currentState) +{ + // Make sure control is hidden or visible at the appropriate times + // while processing a visible or hidden animation it needs to be visible, + // but when finished a hidden operation it needs to be hidden + if (type == ANIM_TYPE_VISIBLE) + { + if (currentProcess == ANIM_PROCESS_REVERSE) + { + if (currentState == ANIM_STATE_APPLIED) + m_visible = HIDDEN; + } + else if (currentProcess == ANIM_PROCESS_NORMAL) + { + if (currentState == ANIM_STATE_DELAYED) + m_visible = DELAYED; + else + m_visible = m_visibleFromSkinCondition ? VISIBLE : HIDDEN; + } + } + else if (type == ANIM_TYPE_HIDDEN) + { + if (currentProcess == ANIM_PROCESS_NORMAL) // a hide animation + { + if (currentState == ANIM_STATE_APPLIED) + m_visible = HIDDEN; // finished + else + m_visible = VISIBLE; // have to be visible until we are finished + } + else if (currentProcess == ANIM_PROCESS_REVERSE) // a visible animation + { // no delay involved here - just make sure it's visible + m_visible = m_visibleFromSkinCondition ? VISIBLE : HIDDEN; + } + } + else if (type == ANIM_TYPE_WINDOW_OPEN) + { + if (currentProcess == ANIM_PROCESS_NORMAL) + { + if (currentState == ANIM_STATE_DELAYED) + m_visible = DELAYED; // delayed + else + m_visible = m_visibleFromSkinCondition ? VISIBLE : HIDDEN; + } + } + else if (type == ANIM_TYPE_FOCUS) + { + // call the focus function if we have finished a focus animation + // (buttons can "click" on focus) + if (currentProcess == ANIM_PROCESS_NORMAL && currentState == ANIM_STATE_APPLIED) + OnFocus(); + } + else if (type == ANIM_TYPE_UNFOCUS) + { + // call the unfocus function if we have finished a focus animation + // (buttons can "click" on focus) + if (currentProcess == ANIM_PROCESS_NORMAL && currentState == ANIM_STATE_APPLIED) + OnUnFocus(); + } +} + +void CGUIControl::Animate(DWORD currentTime) +{ + // check visible state outside the loop, as it could change + GUIVISIBLE visible = m_visible; + m_transform.Reset(); + CPoint center(m_posX + m_width * 0.5f, m_posY + m_height * 0.5f); + for (unsigned int i = 0; i < m_animations.size(); i++) + { + CAnimation &anim = m_animations[i]; + anim.Animate(currentTime, HasRendered() || visible == DELAYED); + // Update the control states (such as visibility) + UpdateStates(anim.GetType(), anim.GetProcess(), anim.GetState()); + // and render the animation effect + anim.RenderAnimation(m_transform, center); + +/* // debug stuff + if (anim.currentProcess != ANIM_PROCESS_NONE) + { + if (anim.effect == EFFECT_TYPE_ZOOM) + { + if (IsVisible()) + CLog::DebugLog("Animating control %d with a %s zoom effect %s. Amount is %2.1f, visible=%s", m_controlID, anim.type == ANIM_TYPE_CONDITIONAL ? (anim.lastCondition ? "conditional_on" : "conditional_off") : (anim.type == ANIM_TYPE_VISIBLE ? "visible" : "hidden"), anim.currentProcess == ANIM_PROCESS_NORMAL ? "normal" : "reverse", anim.amount, IsVisible() ? "true" : "false"); + } + else if (anim.effect == EFFECT_TYPE_FADE) + { + if (IsVisible()) + CLog::DebugLog("Animating control %d with a %s fade effect %s. Amount is %2.1f. Visible=%s", m_controlID, anim.type == ANIM_TYPE_CONDITIONAL ? (anim.lastCondition ? "conditional_on" : "conditional_off") : (anim.type == ANIM_TYPE_VISIBLE ? "visible" : "hidden"), anim.currentProcess == ANIM_PROCESS_NORMAL ? "normal" : "reverse", anim.amount, IsVisible() ? "true" : "false"); + } + }*/ + } + g_graphicsContext.AddTransform(m_transform); +} + +bool CGUIControl::IsAnimating(ANIMATION_TYPE animType) +{ + for (unsigned int i = 0; i < m_animations.size(); i++) + { + CAnimation &anim = m_animations[i]; + if (anim.GetType() == animType) + { + if (anim.GetQueuedProcess() == ANIM_PROCESS_NORMAL) + return true; + if (anim.GetProcess() == ANIM_PROCESS_NORMAL) + return true; + } + else if (anim.GetType() == -animType) + { + if (anim.GetQueuedProcess() == ANIM_PROCESS_REVERSE) + return true; + if (anim.GetProcess() == ANIM_PROCESS_REVERSE) + return true; + } + } + return false; +} + +int CGUIControl::GetNextControl(int direction) const +{ + switch (direction) + { + case ACTION_MOVE_UP: + return m_controlUp; + case ACTION_MOVE_DOWN: + return m_controlDown; + case ACTION_MOVE_LEFT: + return m_controlLeft; + case ACTION_MOVE_RIGHT: + return m_controlRight; + default: + return -1; + } +} + +// input the point with respect to this control to hit, and return +// the control and the point with respect to his control if we have a hit +bool CGUIControl::CanFocusFromPoint(const CPoint &point, CGUIControl **control, CPoint &controlPoint) const +{ + controlPoint = point; + m_transform.InverseTransformPosition(controlPoint.x, controlPoint.y); + if (CanFocus() && HitTest(controlPoint)) + { + *control = (CGUIControl *)this; + return true; + } + *control = NULL; + return false; +} + +void CGUIControl::UnfocusFromPoint(const CPoint &point) +{ + CPoint controlPoint(point); + m_transform.InverseTransformPosition(controlPoint.x, controlPoint.y); + if (!HitTest(controlPoint)) + SetFocus(false); +} + +bool CGUIControl::HasID(int id) const +{ + return GetID() == id; +} + +bool CGUIControl::HasVisibleID(int id) const +{ + return GetID() == id && IsVisible(); +} + +void CGUIControl::SaveStates(vector<CControlState> &states) +{ + // empty for now - do nothing with the majority of controls +} + +void CGUIControl::SetHitRect(const CRect &rect) +{ + m_hitRect = rect; +} + +void CGUIControl::SetCamera(const CPoint &camera) +{ + m_camera = camera; + m_hasCamera = true; +} + +void CGUIControl::ExecuteActions(const vector<CGUIActionDescriptor> &actions) +{ + // we should really save anything we need, as the action may cause the window to close + int savedID = GetID(); + int savedParent = GetParentID(); + vector<CGUIActionDescriptor> savedActions = actions; + + for (unsigned int i = 0; i < savedActions.size(); i++) + { + CGUIMessage message(GUI_MSG_EXECUTE, savedID, savedParent); + message.SetAction(savedActions[i]); + g_graphicsContext.SendMessage(message); + } +} + +CPoint CGUIControl::GetRenderPosition() const +{ + float z = 0; + CPoint point(m_posX, m_posY); + m_transform.TransformPosition(point.x, point.y, z); + if (m_parentControl) + point += m_parentControl->GetRenderPosition(); + return point; +} diff --git a/guilib/GUIControl.h b/guilib/GUIControl.h new file mode 100644 index 0000000000..4de51176c3 --- /dev/null +++ b/guilib/GUIControl.h @@ -0,0 +1,317 @@ +/*! +\file GUIControl.h +\brief +*/ + +#ifndef GUILIB_GUICONTROL_H +#define GUILIB_GUICONTROL_H +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GraphicContext.h" // needed by any rendering operation (all controls) +#include "GUIMessage.h" // needed by practically all controls +#include "GUIFont.h" // needed for the CAngle member (CLabelInfo) among other stuff +#include "VisibleEffect.h" // needed for the CAnimation members +#include "GUIInfoColor.h" // needed for CGuiInfoColor to handle infolabel'ed colors +#include "GUIActionDescriptor.h" + +class CGUIListItem; // forward +class CAction; + +enum ORIENTATION { HORIZONTAL = 0, VERTICAL }; + +class CLabelInfo +{ +public: + CLabelInfo() + { + font = NULL; + align = XBFONT_LEFT; + offsetX = offsetY = 0; + width = 0; + angle = 0; + }; + void UpdateColors() + { + textColor.Update(); + shadowColor.Update(); + selectedColor.Update(); + disabledColor.Update(); + focusedColor.Update(); + }; + + CGUIInfoColor textColor; + CGUIInfoColor shadowColor; + CGUIInfoColor selectedColor; + CGUIInfoColor disabledColor; + CGUIInfoColor focusedColor; + uint32_t align; + float offsetX; + float offsetY; + float width; + float angle; + CGUIFont *font; +}; + +class CControlState +{ +public: + CControlState(int id, int data) + { + m_id = id; + m_data = data; + } + int m_id; + int m_data; +}; + +/*! + \ingroup controls + \brief Base class for controls + */ +class CGUIControl +{ +public: + CGUIControl(); + CGUIControl(int parentID, int controlID, float posX, float posY, float width, float height); + virtual ~CGUIControl(void); + virtual CGUIControl *Clone() const=0; + + virtual void DoRender(DWORD currentTime); + virtual void Render(); + bool HasRendered() const { return m_hasRendered; }; + + // OnAction() is called by our window when we are the focused control. + // We should process any control-specific actions in the derived classes, + // and return true if we have taken care of the action. Returning false + // indicates that the message may be handed down to the window or application + // levels. This base class implementation handles basic movement, and should + // be called from the derived classes when the action has not been handled. + // Return true to indicate that the action has been dealt with. + virtual bool OnAction(const CAction &action); + + // Common actions to make the code easier to read (no ugly switch statements in derived controls) + virtual void OnUp(); + virtual void OnDown(); + virtual void OnLeft(); + virtual void OnRight(); + virtual void OnNextControl(); + virtual void OnPrevControl(); + virtual void OnFocus() {}; + virtual void OnUnFocus() {}; + + /// \brief Called when the mouse is over the control. Default implementation selects the control. + virtual bool OnMouseOver(const CPoint &point); + /// \brief Called when the mouse is dragging over the control. Default implementation does nothing. + virtual bool OnMouseDrag(const CPoint &offset, const CPoint &point) { return false; }; + /// \brief Called when the left mouse button is pressed on the control. Default implementation does nothing. + virtual bool OnMouseClick(int button, const CPoint &point) { return false; }; + /// \brief Called when the left mouse button is pressed on the control. Default implementation does nothing. + virtual bool OnMouseDoubleClick(int button, const CPoint &point) { return false; }; + /// \brief Called when the mouse wheel has moved whilst over the control. Default implementation does nothing + virtual bool OnMouseWheel(char wheel, const CPoint &point) { return false; }; + /// \brief Used to test whether the pointer location (fPosX, fPosY) is inside the control. For mouse events. + virtual bool HitTest(const CPoint &point) const; + /// \brief Focus a control from a screen location. Returns the coordinates of the screen location relative to the control and a pointer to the control. + virtual bool CanFocusFromPoint(const CPoint &point, CGUIControl **control, CPoint &controlPoint) const; + /// \brief Unfocus a control if it's not in a screen location. + virtual void UnfocusFromPoint(const CPoint &point); + + virtual bool OnMessage(CGUIMessage& message); + virtual int GetID(void) const; + void SetID(int id) { m_controlID = id; }; + virtual bool HasID(int id) const; + virtual bool HasVisibleID(int id) const; + int GetParentID() const; + virtual bool HasFocus() const; + virtual void AllocResources(); + virtual void FreeResources(); + virtual void DynamicResourceAlloc(bool bOnOff); + virtual bool IsDynamicallyAllocated() { return false; }; + virtual bool CanFocus() const; + virtual bool IsVisible() const; + bool IsVisibleFromSkin() const { return m_visibleFromSkinCondition; }; + virtual bool IsDisabled() const; + virtual void SetPosition(float posX, float posY); + virtual void SetHitRect(const CRect &rect); + virtual void SetCamera(const CPoint &camera); + void SetColorDiffuse(const CGUIInfoColor &color); + CPoint GetRenderPosition() const; + virtual float GetXPosition() const; + virtual float GetYPosition() const; + virtual float GetWidth() const; + virtual float GetHeight() const; + virtual void SetNavigation(int up, int down, int left, int right); + virtual void SetTabNavigation(int next, int prev); + virtual void SetNavigationActions(const std::vector<CGUIActionDescriptor> &up, const std::vector<CGUIActionDescriptor> &down, + const std::vector<CGUIActionDescriptor> &left, const std::vector<CGUIActionDescriptor> &right); + void ExecuteActions(const std::vector<CGUIActionDescriptor> &actions); + int GetControlIdUp() const { return m_controlUp;}; + int GetControlIdDown() const { return m_controlDown;}; + int GetControlIdLeft() const { return m_controlLeft;}; + int GetControlIdRight() const { return m_controlRight;}; + virtual int GetNextControl(int direction) const; + virtual void SetFocus(bool focus); + virtual void SetWidth(float width); + virtual void SetHeight(float height); + virtual void SetVisible(bool bVisible); + void SetVisibleCondition(int visible, const CGUIInfoBool &allowHiddenFocus); + int GetVisibleCondition() const { return m_visibleCondition; }; + void SetEnableCondition(int condition); + virtual void UpdateVisibility(const CGUIListItem *item = NULL); + virtual void SetInitialVisibility(); + virtual void SetEnabled(bool bEnable); + virtual void SetInvalid() { m_bInvalidated = true; }; + virtual void SetPulseOnSelect(bool pulse) { m_pulseOnSelect = pulse; }; + virtual CStdString GetDescription() const { return ""; }; + + void SetAnimations(const std::vector<CAnimation> &animations); + const std::vector<CAnimation> &GetAnimations() const { return m_animations; }; + + virtual void QueueAnimation(ANIMATION_TYPE anim); + virtual bool IsAnimating(ANIMATION_TYPE anim); + virtual bool HasAnimation(ANIMATION_TYPE anim); + CAnimation *GetAnimation(ANIMATION_TYPE type, bool checkConditions = true); + virtual void ResetAnimation(ANIMATION_TYPE type); + virtual void ResetAnimations(); + + // push information updates + virtual void UpdateInfo(const CGUIListItem *item = NULL) {}; + virtual void SetPushUpdates(bool pushUpdates) { m_pushedUpdates = pushUpdates; }; + + virtual bool IsGroup() const { return false; }; + virtual bool IsContainer() const { return false; }; + virtual bool GetCondition(int condition, int data) const { return false; }; + + void SetParentControl(CGUIControl *control) { m_parentControl = control; }; + CGUIControl *GetParentControl(void) const { return m_parentControl; }; + virtual void SaveStates(std::vector<CControlState> &states); + + enum GUICONTROLTYPES { + GUICONTROL_UNKNOWN, + GUICONTROL_BUTTON, + GUICONTROL_CHECKMARK, + GUICONTROL_FADELABEL, + GUICONTROL_IMAGE, + GUICONTROL_BORDEREDIMAGE, + GUICONTROL_LARGE_IMAGE, + GUICONTROL_LABEL, + GUICONTROL_LIST, + GUICONTROL_LISTGROUP, + GUICONTROL_LISTEX, + GUICONTROL_PROGRESS, + GUICONTROL_RADIO, + GUICONTROL_RSS, + GUICONTROL_SELECTBUTTON, + GUICONTROL_SLIDER, + GUICONTROL_SETTINGS_SLIDER, + GUICONTROL_SPINBUTTON, + GUICONTROL_SPIN, + GUICONTROL_SPINEX, + GUICONTROL_TEXTBOX, + GUICONTROL_THUMBNAIL, + GUICONTROL_TOGGLEBUTTON, + GUICONTROL_VIDEO, + GUICONTROL_MOVER, + GUICONTROL_RESIZE, + GUICONTROL_BUTTONBAR, + GUICONTROL_CONSOLE, + GUICONTROL_EDIT, + GUICONTROL_VISUALISATION, + GUICONTROL_MULTI_IMAGE, + GUICONTROL_GROUP, + GUICONTROL_GROUPLIST, + GUICONTROL_SCROLLBAR, + GUICONTROL_LISTLABEL, + GUICONTROL_MULTISELECT, + GUICONTAINER_LIST, + GUICONTAINER_WRAPLIST, + GUICONTAINER_FIXEDLIST, + GUICONTAINER_PANEL + }; + GUICONTROLTYPES GetControlType() const { return ControlType; } + + enum GUIVISIBLE { HIDDEN = 0, DELAYED, VISIBLE }; + +#ifdef _DEBUG + virtual void DumpTextureUse() {}; +#endif +protected: + virtual void UpdateColors(); + virtual void Animate(DWORD currentTime); + virtual bool CheckAnimation(ANIMATION_TYPE animType); + void UpdateStates(ANIMATION_TYPE type, ANIMATION_PROCESS currentProcess, ANIMATION_STATE currentState); + bool SendWindowMessage(CGUIMessage &message); + + // navigation + int m_controlLeft; + int m_controlRight; + int m_controlUp; + int m_controlDown; + int m_controlNext; + int m_controlPrev; + + std::vector<CGUIActionDescriptor> m_leftActions; + std::vector<CGUIActionDescriptor> m_rightActions; + std::vector<CGUIActionDescriptor> m_upActions; + std::vector<CGUIActionDescriptor> m_downActions; + std::vector<CGUIActionDescriptor> m_nextActions; + std::vector<CGUIActionDescriptor> m_prevActions; + + float m_posX; + float m_posY; + float m_height; + float m_width; + CRect m_hitRect; + CGUIInfoColor m_diffuseColor; + int m_controlID; + int m_parentID; + bool m_bHasFocus; + bool m_bInvalidated; + bool m_bAllocated; + bool m_pulseOnSelect; + GUICONTROLTYPES ControlType; + + CGUIControl *m_parentControl; // our parent control if we're part of a group + + // visibility condition/state + int m_visibleCondition; + GUIVISIBLE m_visible; + bool m_visibleFromSkinCondition; + bool m_forceHidden; // set from the code when a hidden operation is given - overrides m_visible + CGUIInfoBool m_allowHiddenFocus; + bool m_hasRendered; + // enable/disable state + int m_enableCondition; + bool m_enabled; + + bool m_pushedUpdates; + + // animation effects + std::vector<CAnimation> m_animations; + CPoint m_camera; + bool m_hasCamera; + TransformMatrix m_transform; +}; + +#endif diff --git a/guilib/GUIControlFactory.cpp b/guilib/GUIControlFactory.cpp new file mode 100644 index 0000000000..28022b746c --- /dev/null +++ b/guilib/GUIControlFactory.cpp @@ -0,0 +1,1415 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControlFactory.h" +#include "LocalizeStrings.h" +#include "GUIButtonControl.h" +#include "GUIRadioButtonControl.h" +#include "GUISpinControl.h" +#include "GUIRSSControl.h" +#include "GUIImage.h" +#include "GUIBorderedImage.h" +#include "GUILabelControl.h" +#include "GUIEditControl.h" +#include "GUIFadeLabelControl.h" +#include "GUICheckMarkControl.h" +#include "GUIToggleButtonControl.h" +#include "GUITextBox.h" +#include "GUIVideoControl.h" +#include "GUIProgressControl.h" +#include "GUISliderControl.h" +#include "GUISelectButtonControl.h" +#include "GUIMoverControl.h" +#include "GUIResizeControl.h" +#include "GUIButtonScroller.h" +#include "GUISpinControlEx.h" +#include "GUIVisualisationControl.h" +#include "GUISettingsSliderControl.h" +#include "GUIMultiImage.h" +#include "GUIControlGroup.h" +#include "GUIControlGroupList.h" +#include "GUIScrollBarControl.h" +#include "GUIListContainer.h" +#include "GUIFixedListContainer.h" +#include "GUIWrappingListContainer.h" +#include "GUIPanelContainer.h" +#include "GUIMultiSelectText.h" +#include "GUIListLabel.h" +#include "GUIListGroup.h" +#include "utils/GUIInfoManager.h" +#include "utils/CharsetConverter.h" +#include "utils/log.h" +#include "ButtonTranslator.h" +#include "XMLUtils.h" +#include "GUIFontManager.h" +#include "GUIColorManager.h" +#include "SkinInfo.h" +#include "Settings.h" +#include "StringUtils.h" + +using namespace std; + +CGUIControlFactory::CGUIControlFactory(void) +{} + +CGUIControlFactory::~CGUIControlFactory(void) +{} + +bool CGUIControlFactory::GetIntRange(const TiXmlNode* pRootNode, const char* strTag, int& iMinValue, int& iMaxValue, int& iIntervalValue) +{ + const TiXmlNode* pNode = pRootNode->FirstChild(strTag); + if (!pNode || !pNode->FirstChild()) return false; + iMinValue = atoi(pNode->FirstChild()->Value()); + const char* maxValue = strchr(pNode->FirstChild()->Value(), ','); + if (maxValue) + { + maxValue++; + iMaxValue = atoi(maxValue); + + const char* intervalValue = strchr(maxValue, ','); + if (intervalValue) + { + intervalValue++; + iIntervalValue = atoi(intervalValue); + } + } + + return true; +} + +bool CGUIControlFactory::GetFloatRange(const TiXmlNode* pRootNode, const char* strTag, float& fMinValue, float& fMaxValue, float& fIntervalValue) +{ + const TiXmlNode* pNode = pRootNode->FirstChild(strTag); + if (!pNode || !pNode->FirstChild()) return false; + fMinValue = (float)atof(pNode->FirstChild()->Value()); + const char* maxValue = strchr(pNode->FirstChild()->Value(), ','); + if (maxValue) + { + maxValue++; + fMaxValue = (float)atof(maxValue); + + const char* intervalValue = strchr(maxValue, ','); + if (intervalValue) + { + intervalValue++; + fIntervalValue = (float)atof(intervalValue); + } + } + + return true; +} + +bool CGUIControlFactory::GetFloat(const TiXmlNode* pRootNode, const char* strTag, float& value) +{ + const TiXmlNode* pNode = pRootNode->FirstChild(strTag ); + if (!pNode || !pNode->FirstChild()) return false; + return g_SkinInfo.ResolveConstant(pNode->FirstChild()->Value(), value); +} + +bool CGUIControlFactory::GetDWORD(const TiXmlNode* pRootNode, const char* strTag, DWORD &value) +{ + const TiXmlNode* pNode = pRootNode->FirstChild(strTag ); + if (!pNode || !pNode->FirstChild()) return false; + return g_SkinInfo.ResolveConstant(pNode->FirstChild()->Value(), value); +} + +bool CGUIControlFactory::GetMultipleString(const TiXmlNode* pRootNode, const char* strTag, std::vector<CGUIActionDescriptor>& vecStringValue) +{ + const TiXmlNode* pNode = pRootNode->FirstChild(strTag ); + if (!pNode) return false; + vecStringValue.clear(); + bool bFound = false; + while (pNode) + { + CGUIActionDescriptor action; + if (CGUIControlFactory::GetAction((const TiXmlElement*) pNode, action)) + { + vecStringValue.push_back(action); + bFound = true; + } + pNode = pNode->NextSibling(strTag); + } + return bFound; +} + +bool CGUIControlFactory::GetAction(const TiXmlElement* pElement, CGUIActionDescriptor &action) +{ + CStdString langStr = pElement->Attribute("lang"); + if (langStr.CompareNoCase("python") == 0 ) + action.m_lang = CGUIActionDescriptor::LANG_PYTHON; + else + action.m_lang = CGUIActionDescriptor::LANG_XBMC; + + if (pElement->FirstChild()) + { + action.m_action = pElement->FirstChild()->Value(); + return true; + } + else + { + action.m_action = ""; + return false; + } +} + +bool CGUIControlFactory::GetAspectRatio(const TiXmlNode* pRootNode, const char* strTag, CAspectRatio &aspect) +{ + CStdString ratio; + const TiXmlElement *node = pRootNode->FirstChildElement(strTag); + if (!node || !node->FirstChild()) + return false; + + ratio = node->FirstChild()->Value(); + if (ratio.CompareNoCase("keep") == 0) aspect.ratio = CAspectRatio::AR_KEEP; + else if (ratio.CompareNoCase("scale") == 0) aspect.ratio = CAspectRatio::AR_SCALE; + else if (ratio.CompareNoCase("center") == 0) aspect.ratio = CAspectRatio::AR_CENTER; + else if (ratio.CompareNoCase("stretch") == 0) aspect.ratio = CAspectRatio::AR_STRETCH; + + const char *attribute = node->Attribute("align"); + if (attribute) + { + CStdString align(attribute); + if (align.CompareNoCase("center") == 0) aspect.align = ASPECT_ALIGN_CENTER | (aspect.align & ASPECT_ALIGNY_MASK); + else if (align.CompareNoCase("right") == 0) aspect.align = ASPECT_ALIGN_RIGHT | (aspect.align & ASPECT_ALIGNY_MASK); + else if (align.CompareNoCase("left") == 0) aspect.align = ASPECT_ALIGN_LEFT | (aspect.align & ASPECT_ALIGNY_MASK); + } + attribute = node->Attribute("aligny"); + if (attribute) + { + CStdString align(attribute); + if (align.CompareNoCase("center") == 0) aspect.align = ASPECT_ALIGNY_CENTER | (aspect.align & ASPECT_ALIGN_MASK); + else if (align.CompareNoCase("bottom") == 0) aspect.align = ASPECT_ALIGNY_BOTTOM | (aspect.align & ASPECT_ALIGN_MASK); + else if (align.CompareNoCase("top") == 0) aspect.align = ASPECT_ALIGNY_TOP | (aspect.align & ASPECT_ALIGN_MASK); + } + attribute = node->Attribute("scalediffuse"); + if (attribute) + { + CStdString scale(attribute); + if (scale.CompareNoCase("true") == 0 || scale.CompareNoCase("yes") == 0) + aspect.scaleDiffuse = true; + else + aspect.scaleDiffuse = false; + } + return true; +} + +bool CGUIControlFactory::GetInfoTexture(const TiXmlNode* pRootNode, const char* strTag, CTextureInfo &image, CGUIInfoLabel &info) +{ + GetTexture(pRootNode, strTag, image); + image.filename = ""; + GetInfoLabel(pRootNode, strTag, info); + return true; +} + +bool CGUIControlFactory::GetTexture(const TiXmlNode* pRootNode, const char* strTag, CTextureInfo &image) +{ + const TiXmlElement* pNode = pRootNode->FirstChildElement(strTag); + if (!pNode) return false; + const char *border = pNode->Attribute("border"); + if (border) + GetRectFromString(border, image.border); + image.orientation = 0; + const char *flipX = pNode->Attribute("flipx"); + if (flipX && strcmpi(flipX, "true") == 0) image.orientation = 1; + const char *flipY = pNode->Attribute("flipy"); + if (flipY && strcmpi(flipY, "true") == 0) image.orientation = 3 - image.orientation; // either 3 or 2 + image.diffuse = pNode->Attribute("diffuse"); + const char *background = pNode->Attribute("background"); + if (background && strnicmp(background, "true", 4) == 0) + image.useLarge = true; + image.filename = (pNode->FirstChild() && pNode->FirstChild()->ValueStr() != "-") ? pNode->FirstChild()->Value() : ""; + return true; +} + +void CGUIControlFactory::GetRectFromString(const CStdString &string, FRECT &rect) +{ + // format is rect="left,right,top,bottom" + CStdStringArray strRect; + StringUtils::SplitString(string, ",", strRect); + if (strRect.size() == 1) + { + g_SkinInfo.ResolveConstant(strRect[0], rect.left); + rect.top = rect.left; + rect.right = rect.left; + rect.bottom = rect.left; + } + else if (strRect.size() == 4) + { + g_SkinInfo.ResolveConstant(strRect[0], rect.left); + g_SkinInfo.ResolveConstant(strRect[1], rect.top); + g_SkinInfo.ResolveConstant(strRect[2], rect.right); + g_SkinInfo.ResolveConstant(strRect[3], rect.bottom); + } +} + +bool CGUIControlFactory::GetAlignment(const TiXmlNode* pRootNode, const char* strTag, uint32_t& alignment) +{ + const TiXmlNode* pNode = pRootNode->FirstChild(strTag); + if (!pNode || !pNode->FirstChild()) return false; + + CStdString strAlign = pNode->FirstChild()->Value(); + if (strAlign == "right" || strAlign == "bottom") alignment = XBFONT_RIGHT; + else if (strAlign == "center") alignment = XBFONT_CENTER_X; + else if (strAlign == "justify") alignment = XBFONT_JUSTIFIED; + else alignment = XBFONT_LEFT; + return true; +} + +bool CGUIControlFactory::GetAlignmentY(const TiXmlNode* pRootNode, const char* strTag, uint32_t& alignment) +{ + const TiXmlNode* pNode = pRootNode->FirstChild(strTag ); + if (!pNode || !pNode->FirstChild()) + { + return false; + } + + CStdString strAlign = pNode->FirstChild()->Value(); + + alignment = 0; + if (strAlign == "center") + { + alignment = XBFONT_CENTER_Y; + } + + return true; +} + +bool CGUIControlFactory::GetConditionalVisibility(const TiXmlNode* control, int &condition, CGUIInfoBool &allowHiddenFocus) +{ + const TiXmlElement* node = control->FirstChildElement("visible"); + if (!node) return false; + vector<CStdString> conditions; + while (node) + { + const char *hidden = node->Attribute("allowhiddenfocus"); + if (hidden) + allowHiddenFocus.Parse(hidden); + // add to our condition string + if (!node->NoChildren()) + conditions.push_back(node->FirstChild()->Value()); + node = node->NextSiblingElement("visible"); + } + if (!conditions.size()) + return false; + if (conditions.size() == 1) + condition = g_infoManager.TranslateString(conditions[0]); + else + { // multiple conditions should be anded together + CStdString conditionString = "["; + for (unsigned int i = 0; i < conditions.size() - 1; i++) + conditionString += conditions[i] + "] + ["; + conditionString += conditions[conditions.size() - 1] + "]"; + condition = g_infoManager.TranslateString(conditionString); + } + return (condition != 0); +} + +bool CGUIControlFactory::GetCondition(const TiXmlNode *control, const char *tag, int &condition) +{ + CStdString condString; + if (XMLUtils::GetString(control, tag, condString)) + { + condition = g_infoManager.TranslateString(condString); + return true; + } + return false; +} + +bool CGUIControlFactory::GetConditionalVisibility(const TiXmlNode *control, int &condition) +{ + CGUIInfoBool allowHiddenFocus; + return GetConditionalVisibility(control, condition, allowHiddenFocus); +} + +bool CGUIControlFactory::GetAnimations(const TiXmlNode *control, const FRECT &rect, vector<CAnimation> &animations) +{ + const TiXmlElement* node = control->FirstChildElement("animation"); + bool ret = false; + if (node) + animations.clear(); + while (node) + { + ret = true; + if (node->FirstChild()) + { + CAnimation anim; + anim.Create(node, rect); + animations.push_back(anim); + if (strcmpi(node->FirstChild()->Value(), "VisibleChange") == 0) + { // add the hidden one as well + TiXmlElement hidden(*node); + hidden.FirstChild()->SetValue("hidden"); + const char *start = hidden.Attribute("start"); + const char *end = hidden.Attribute("end"); + if (start && end) + { + CStdString temp = end; + hidden.SetAttribute("end", start); + hidden.SetAttribute("start", temp.c_str()); + } + else if (start) + hidden.SetAttribute("end", start); + else if (end) + hidden.SetAttribute("start", end); + CAnimation anim2; + anim2.Create(&hidden, rect); + animations.push_back(anim2); + } + } + node = node->NextSiblingElement("animation"); + } + return ret; +} + +bool CGUIControlFactory::GetHitRect(const TiXmlNode *control, CRect &rect) +{ + const TiXmlElement* node = control->FirstChildElement("hitrect"); + if (node) + { + if (node->Attribute("x")) g_SkinInfo.ResolveConstant(node->Attribute("x"), rect.x1); + if (node->Attribute("y")) g_SkinInfo.ResolveConstant(node->Attribute("y"), rect.y1); + if (node->Attribute("w")) + { + g_SkinInfo.ResolveConstant(node->Attribute("w"), rect.x2); + rect.x2 += rect.x1; + } + if (node->Attribute("h")) + { + g_SkinInfo.ResolveConstant(node->Attribute("h"), rect.y2); + rect.y2 += rect.y1; + } + return true; + } + return false; +} + +bool CGUIControlFactory::GetColor(const TiXmlNode *control, const char *strTag, color_t &value) +{ + const TiXmlElement* node = control->FirstChildElement(strTag); + if (node && node->FirstChild()) + { + value = g_colorManager.GetColor(node->FirstChild()->Value()); + return true; + } + return false; +} + +bool CGUIControlFactory::GetInfoColor(const TiXmlNode *control, const char *strTag, CGUIInfoColor &value) +{ + const TiXmlElement* node = control->FirstChildElement(strTag); + if (node && node->FirstChild()) + { + value.Parse(node->FirstChild()->Value()); + return true; + } + return false; +} + +bool CGUIControlFactory::GetNavigation(const TiXmlElement *node, const char *tag, int &direction, vector<CGUIActionDescriptor> &actions) +{ + if (!GetMultipleString(node, tag, actions)) + return false; // no tag specified + if (actions.size() == 1 && StringUtils::IsNaturalNumber(actions[0].m_action)) + { // single numeric tag specified + direction = atol(actions[0].m_action.c_str()); + actions.clear(); + } + else + direction = 0; + return true; +} + +void CGUIControlFactory::GetInfoLabel(const TiXmlNode *pControlNode, const CStdString &labelTag, CGUIInfoLabel &infoLabel) +{ + vector<CGUIInfoLabel> labels; + GetInfoLabels(pControlNode, labelTag, labels); + if (labels.size()) + infoLabel = labels[0]; +} + +void CGUIControlFactory::GetInfoLabels(const TiXmlNode *pControlNode, const CStdString &labelTag, vector<CGUIInfoLabel> &infoLabels) +{ + // we can have the following infolabels: + // 1. <number>1234</number> -> direct number + // 2. <label>number</label> -> lookup in localizestrings + // 3. <label fallback="blah">$LOCALIZE(blah) $INFO(blah)</label> -> infolabel with given fallback + // 4. <info>ListItem.Album</info> (uses <label> as fallback) + int labelNumber = 0; + if (XMLUtils::GetInt(pControlNode, "number", labelNumber)) + { + CStdString label; + label.Format("%i", labelNumber); + infoLabels.push_back(CGUIInfoLabel(label)); + return; // done + } + const TiXmlElement *labelNode = pControlNode->FirstChildElement(labelTag); + while (labelNode) + { + if (labelNode->FirstChild()) + { + CStdString label = labelNode->FirstChild()->Value(); + CStdString fallback = labelNode->Attribute("fallback"); + if (label.size() && label[0] != '-') + { + if (StringUtils::IsNaturalNumber(label)) + label = g_localizeStrings.Get(atoi(label)); + else // we assume the skin xml's aren't encoded as UTF-8 + g_charsetConverter.unknownToUTF8(label); + if (StringUtils::IsNaturalNumber(fallback)) + fallback = g_localizeStrings.Get(atoi(fallback)); + else + g_charsetConverter.unknownToUTF8(fallback); + infoLabels.push_back(CGUIInfoLabel(label, fallback)); + } + } + labelNode = labelNode->NextSiblingElement(labelTag); + } + const TiXmlNode *infoNode = pControlNode->FirstChild("info"); + if (infoNode) + { // <info> nodes override <label>'s (backward compatibility) + CStdString fallback; + if (infoLabels.size()) + fallback = infoLabels[0].GetLabel(0); + infoLabels.clear(); + while (infoNode) + { + if (infoNode->FirstChild()) + { + CStdString info; + info.Format("$INFO[%s]", infoNode->FirstChild()->Value()); + infoLabels.push_back(CGUIInfoLabel(info, fallback)); + } + infoNode = infoNode->NextSibling("info"); + } + } +} + +// Convert a string to a GUI label, by translating/parsing the label for localisable strings +CStdString CGUIControlFactory::FilterLabel(const CStdString &label) +{ + CStdString viewLabel = label; + if (StringUtils::IsNaturalNumber(viewLabel)) + viewLabel = g_localizeStrings.Get(atoi(label)); + else + g_charsetConverter.unknownToUTF8(viewLabel); + return viewLabel; +} + +bool CGUIControlFactory::GetString(const TiXmlNode* pRootNode, const char *strTag, CStdString &text) +{ + if (!XMLUtils::GetString(pRootNode, strTag, text)) + return false; + if (text == "-") + text.Empty(); + if (StringUtils::IsNaturalNumber(text)) + text = g_localizeStrings.Get(atoi(text.c_str())); + else + g_charsetConverter.unknownToUTF8(text); + return true; +} + +CStdString CGUIControlFactory::GetType(const TiXmlElement *pControlNode) +{ + CStdString type; + const char *szType = pControlNode->Attribute("type"); + if (szType) + type = szType; + else // backward compatibility - not desired + XMLUtils::GetString(pControlNode, "type", type); + return type; +} + +CGUIControl* CGUIControlFactory::Create(int parentID, const FRECT &rect, TiXmlElement* pControlNode, bool insideContainer) +{ + // resolve any <include> tag's in this control + g_SkinInfo.ResolveIncludes(pControlNode); + + // get the control type + CStdString strType = GetType(pControlNode); + + // resolve again with strType set so that <default> tags are added + g_SkinInfo.ResolveIncludes(pControlNode, strType); + + int id = 0; + float posX = 0, posY = 0; + float width = 0, height = 0; + + int left = 0, right = 0, up = 0, down = 0, next = 0, prev = 0; + vector<CGUIActionDescriptor> leftActions, rightActions, upActions, downActions, nextActions, prevActions; + + int pageControl = 0; + CGUIInfoColor colorDiffuse(0xFFFFFFFF); + int defaultControl = 0; + bool defaultAlways = false; + CStdString strTmp; + int singleInfo = 0; + CStdString strLabel; + int iUrlSet=0; + int iToggleSelect; + + float spinWidth = 16; + float spinHeight = 16; + float spinPosX = 0, spinPosY = 0; + float checkWidth = 0, checkHeight = 0; + CStdString strSubType; + int iType = SPIN_CONTROL_TYPE_TEXT; + int iMin = 0; + int iMax = 100; + int iInterval = 1; + float fMin = 0.0f; + float fMax = 1.0f; + float fInterval = 0.1f; + bool bReverse = true; + bool bReveal = false; + CTextureInfo textureBackground, textureLeft, textureRight, textureMid, textureOverlay; + float rMin = 0.0f; + float rMax = 100.0f; + CTextureInfo textureNib, textureNibFocus, textureBar, textureBarFocus; + CTextureInfo textureLeftFocus, textureRightFocus; + CTextureInfo textureUp, textureDown; + CTextureInfo textureUpFocus, textureDownFocus; + CTextureInfo texture, borderTexture; + CGUIInfoLabel textureFile; + CTextureInfo textureCheckMark, textureCheckMarkNF; + CTextureInfo textureFocus, textureNoFocus; + CTextureInfo textureAltFocus, textureAltNoFocus; + CTextureInfo textureRadioOn, textureRadioOff; + CTextureInfo imageNoFocus, imageFocus; + CGUIInfoLabel texturePath; + FRECT borderSize = { 0, 0, 0, 0}; + + float itemWidth = 16, itemHeight = 16; + float sliderWidth = 150, sliderHeight = 16; + float textureWidthBig = 128; + float textureHeightBig = 128; + float textureHeight = 30; + float textureWidth = 80; + float itemWidthBig = 150; + float itemHeightBig = 150; + + float spaceBetweenItems = 2; + bool bHasPath = false; + vector<CGUIActionDescriptor> clickActions; + vector<CGUIActionDescriptor> altclickActions; + vector<CGUIActionDescriptor> focusActions; + vector<CGUIActionDescriptor> unfocusActions; + vector<CGUIActionDescriptor> textChangeActions; + CStdString strTitle = ""; + CStdString strRSSTags = ""; + + float thumbXPos = 4; + float thumbYPos = 10; + float thumbWidth = 64; + float thumbHeight = 64; + + float thumbXPosBig = 14; + float thumbYPosBig = 14; + float thumbWidthBig = 100; + float thumbHeightBig = 100; + int iNumSlots = 7; + float buttonGap = 5; + int iDefaultSlot = 2; + int iMovementRange = 2; + bool bHorizontal = false; + int iAlpha = 0; + bool bWrapAround = true; + bool bSmoothScrolling = true; + CAspectRatio aspect; +#ifdef PRE_SKIN_VERSION_9_10_COMPATIBILITY + if (insideContainer) // default for inside containers is keep + aspect.ratio = CAspectRatio::AR_KEEP; +#endif + + int iVisibleCondition = 0; + CGUIInfoBool allowHiddenFocus(false); + int enableCondition = 0; + + vector<CAnimation> animations; + + bool bScrollLabel = false; + bool bPulse = true; + DWORD timePerImage = 0; + DWORD fadeTime = 0; + DWORD timeToPauseAtEnd = 0; + bool randomized = false; + bool loop = true; + bool wrapMultiLine = false; + ORIENTATION orientation = VERTICAL; + bool showOnePage = true; + bool scrollOut = true; + int preloadItems = 0; + + CLabelInfo labelInfo; + CLabelInfo labelInfo2; + CLabelInfo spinInfo; + + CGUIInfoColor textColor3; + + float radioWidth = 0; + float radioHeight = 0; + float radioPosX = 0; + float radioPosY = 0; + + CStdString altLabel; + CStdString strLabel2; + + int focusPosition = 0; + int scrollTime = 200; + bool useControlCoords = false; + bool renderFocusedLast = false; + + CRect hitRect; + CPoint camera; + bool hasCamera = false; + int scrollSpeed = CScrollInfo::defaultSpeed; + bool resetOnLabelChange = true; + bool bPassword = false; + + ///////////////////////////////////////////////////////////////////////////// + // Read control properties from XML + // + + if (!pControlNode->Attribute("id", (int*) &id)) + XMLUtils::GetInt(pControlNode, "id", (int&) id); // backward compatibility - not desired + // TODO: Perhaps we should check here whether id is valid for focusable controls + // such as buttons etc. For labels/fadelabels/images it does not matter + + GetFloat(pControlNode, "posx", posX); + GetFloat(pControlNode, "posy", posY); + // Convert these from relative coords + CStdString pos; + XMLUtils::GetString(pControlNode, "posx", pos); + if (pos.Right(1) == "r") + posX = (rect.right - rect.left) - posX; + XMLUtils::GetString(pControlNode, "posy", pos); + if (pos.Right(1) == "r") + posY = (rect.bottom - rect.top) - posY; + + GetFloat(pControlNode, "width", width); + GetFloat(pControlNode, "height", height); + + // adjust width and height accordingly for groups. Groups should + // take the width/height of the parent (adjusted for positioning) + // if none is defined. + if (strType == "group" || strType == "grouplist") + { + if (!width) + width = max(rect.right - posX, 0.0f); + if (!height) + height = max(rect.bottom - posY, 0.0f); + } + + hitRect.SetRect(posX, posY, posX + width, posY + height); + GetHitRect(pControlNode, hitRect); + + if (!GetNavigation(pControlNode, "onup", up, upActions)) up = id - 1; + if (!GetNavigation(pControlNode, "ondown", down, downActions)) down = id + 1; + if (!GetNavigation(pControlNode, "onleft", left, leftActions)) left = id; + if (!GetNavigation(pControlNode, "onright", right, rightActions)) right = id; + if (!GetNavigation(pControlNode, "onnext", next, nextActions)) next = id; + if (!GetNavigation(pControlNode, "onprev", prev, prevActions)) prev = id; + + if (XMLUtils::GetInt(pControlNode, "defaultcontrol", defaultControl)) + { + const char *always = pControlNode->FirstChildElement("defaultcontrol")->Attribute("always"); + if (always && strnicmp(always, "true", 4) == 0) + defaultAlways = true; + } + XMLUtils::GetInt(pControlNode, "pagecontrol", pageControl); + + GetInfoColor(pControlNode, "colordiffuse", colorDiffuse); + + GetConditionalVisibility(pControlNode, iVisibleCondition, allowHiddenFocus); + GetCondition(pControlNode, "enable", enableCondition); + + // note: animrect here uses .right and .bottom as width and height respectively (nonstandard) + FRECT animRect = { posX, posY, width, height }; + GetAnimations(pControlNode, animRect, animations); + + GetInfoColor(pControlNode, "textcolor", labelInfo.textColor); + GetInfoColor(pControlNode, "focusedcolor", labelInfo.focusedColor); + GetInfoColor(pControlNode, "disabledcolor", labelInfo.disabledColor); + GetInfoColor(pControlNode, "shadowcolor", labelInfo.shadowColor); + GetInfoColor(pControlNode, "selectedcolor", labelInfo.selectedColor); + GetFloat(pControlNode, "textoffsetx", labelInfo.offsetX); + GetFloat(pControlNode, "textoffsety", labelInfo.offsetY); + GetFloat(pControlNode, "textxoff", labelInfo.offsetX); + GetFloat(pControlNode, "textyoff", labelInfo.offsetY); + GetFloat(pControlNode, "textxoff2", labelInfo2.offsetX); + GetFloat(pControlNode, "textyoff2", labelInfo2.offsetY); + int angle = 0; // use the negative angle to compensate for our vertically flipped cartesian plane + if (XMLUtils::GetInt(pControlNode, "angle", angle)) labelInfo.angle = (float)-angle; + CStdString strFont; + if (XMLUtils::GetString(pControlNode, "font", strFont)) + labelInfo.font = g_fontManager.GetFont(strFont); + GetAlignment(pControlNode, "align", labelInfo.align); + uint32_t alignY = 0; + if (GetAlignmentY(pControlNode, "aligny", alignY)) + labelInfo.align |= alignY; + if (GetFloat(pControlNode, "textwidth", labelInfo.width)) + labelInfo.align |= XBFONT_TRUNCATED; + labelInfo2.selectedColor = labelInfo.selectedColor; + GetInfoColor(pControlNode, "selectedcolor2", labelInfo2.selectedColor); + GetInfoColor(pControlNode, "textcolor2", labelInfo2.textColor); + GetInfoColor(pControlNode, "focusedcolor2", labelInfo2.focusedColor); + labelInfo2.font = labelInfo.font; + if (XMLUtils::GetString(pControlNode, "font2", strFont)) + labelInfo2.font = g_fontManager.GetFont(strFont); + + GetMultipleString(pControlNode, "onclick", clickActions); + GetMultipleString(pControlNode, "ontextchange", textChangeActions); + GetMultipleString(pControlNode, "onfocus", focusActions); + GetMultipleString(pControlNode, "onunfocus", unfocusActions); + GetMultipleString(pControlNode, "altclick", altclickActions); + + CStdString infoString; + if (XMLUtils::GetString(pControlNode, "info", infoString)) + singleInfo = g_infoManager.TranslateString(infoString); + + GetTexture(pControlNode, "texturefocus", textureFocus); + GetTexture(pControlNode, "texturenofocus", textureNoFocus); + GetTexture(pControlNode, "alttexturefocus", textureAltFocus); + GetTexture(pControlNode, "alttexturenofocus", textureAltNoFocus); + CStdString strToggleSelect; + XMLUtils::GetString(pControlNode, "usealttexture", strToggleSelect); + XMLUtils::GetString(pControlNode, "selected", strToggleSelect); + iToggleSelect = g_infoManager.TranslateString(strToggleSelect); + + XMLUtils::GetBoolean(pControlNode, "haspath", bHasPath); + + GetTexture(pControlNode, "textureup", textureUp); + GetTexture(pControlNode, "texturedown", textureDown); + GetTexture(pControlNode, "textureupfocus", textureUpFocus); + GetTexture(pControlNode, "texturedownfocus", textureDownFocus); + + GetTexture(pControlNode, "textureleft", textureLeft); + GetTexture(pControlNode, "textureright", textureRight); + GetTexture(pControlNode, "textureleftfocus", textureLeftFocus); + GetTexture(pControlNode, "texturerightfocus", textureRightFocus); + + GetInfoColor(pControlNode, "spincolor", spinInfo.textColor); + if (XMLUtils::GetString(pControlNode, "spinfont", strFont)) + spinInfo.font = g_fontManager.GetFont(strFont); + if (!spinInfo.font) spinInfo.font = labelInfo.font; + + GetFloat(pControlNode, "spinwidth", spinWidth); + GetFloat(pControlNode, "spinheight", spinHeight); + GetFloat(pControlNode, "spinposx", spinPosX); + GetFloat(pControlNode, "spinposy", spinPosY); + + GetFloat(pControlNode, "markwidth", checkWidth); + GetFloat(pControlNode, "markheight", checkHeight); + GetFloat(pControlNode, "sliderwidth", sliderWidth); + GetFloat(pControlNode, "sliderheight", sliderHeight); + GetTexture(pControlNode, "texturecheckmark", textureCheckMark); + GetTexture(pControlNode, "texturecheckmarknofocus", textureCheckMarkNF); + GetTexture(pControlNode, "textureradiofocus", textureRadioOn); // backward compatibility + GetTexture(pControlNode, "textureradionofocus", textureRadioOff); + GetTexture(pControlNode, "textureradioon", textureRadioOn); + GetTexture(pControlNode, "textureradiooff", textureRadioOff); + + GetTexture(pControlNode, "texturesliderbackground", textureBackground); + GetTexture(pControlNode, "texturesliderbar", textureBar); + GetTexture(pControlNode, "texturesliderbarfocus", textureBarFocus); + GetTexture(pControlNode, "textureslidernib", textureNib); + GetTexture(pControlNode, "textureslidernibfocus", textureNibFocus); + + XMLUtils::GetString(pControlNode, "title", strTitle); + XMLUtils::GetString(pControlNode, "tagset", strRSSTags); + GetInfoColor(pControlNode, "headlinecolor", labelInfo2.textColor); + GetInfoColor(pControlNode, "titlecolor", textColor3); + + if (XMLUtils::GetString(pControlNode, "subtype", strSubType)) + { + strSubType.ToLower(); + + if ( strSubType == "int") + iType = SPIN_CONTROL_TYPE_INT; + else if ( strSubType == "page") + iType = SPIN_CONTROL_TYPE_PAGE; + else if ( strSubType == "float") + iType = SPIN_CONTROL_TYPE_FLOAT; + else + iType = SPIN_CONTROL_TYPE_TEXT; + } + + if (!GetIntRange(pControlNode, "range", iMin, iMax, iInterval)) + { + GetFloatRange(pControlNode, "range", fMin, fMax, fInterval); + } + + XMLUtils::GetBoolean(pControlNode, "reverse", bReverse); + XMLUtils::GetBoolean(pControlNode, "reveal", bReveal); + + GetTexture(pControlNode, "texturebg", textureBackground); + GetTexture(pControlNode, "lefttexture", textureLeft); + GetTexture(pControlNode, "midtexture", textureMid); + GetTexture(pControlNode, "righttexture", textureRight); + GetTexture(pControlNode, "overlaytexture", textureOverlay); + + // the <texture> tag can be overridden by the <info> tag + GetInfoTexture(pControlNode, "texture", texture, textureFile); + + GetTexture(pControlNode, "bordertexture", borderTexture); + GetFloat(pControlNode, "rangemin", rMin); + GetFloat(pControlNode, "rangemax", rMax); + + GetFloat(pControlNode, "itemwidth", itemWidth); + GetFloat(pControlNode, "itemheight", itemHeight); + GetFloat(pControlNode, "spacebetweenitems", spaceBetweenItems); + + GetTexture(pControlNode, "imagefolder", imageNoFocus); + GetTexture(pControlNode, "imagefolderfocus", imageFocus); + GetFloat(pControlNode, "texturewidth", textureWidth); + GetFloat(pControlNode, "textureheight", textureHeight); + + GetFloat(pControlNode, "thumbwidth", thumbWidth); + GetFloat(pControlNode, "thumbheight", thumbHeight); + GetFloat(pControlNode, "thumbposx", thumbXPos); + GetFloat(pControlNode, "thumbposy", thumbYPos); + + GetFloat(pControlNode, "thumbwidthbig", thumbWidthBig); + GetFloat(pControlNode, "thumbheightbig", thumbHeightBig); + GetFloat(pControlNode, "thumbposxbig", thumbXPosBig); + GetFloat(pControlNode, "thumbposybig", thumbYPosBig); + + GetFloat(pControlNode, "texturewidthbig", textureWidthBig); + GetFloat(pControlNode, "textureheightbig", textureHeightBig); + GetFloat(pControlNode, "itemwidthbig", itemWidthBig); + GetFloat(pControlNode, "itemheightbig", itemHeightBig); + + // fade label can have a whole bunch, but most just have one + vector<CGUIInfoLabel> infoLabels; + GetInfoLabels(pControlNode, "label", infoLabels); + + GetString(pControlNode, "label", strLabel); + GetString(pControlNode, "altlabel", altLabel); + GetString(pControlNode, "label2", strLabel2); + + XMLUtils::GetBoolean(pControlNode, "wrapmultiline", wrapMultiLine); + XMLUtils::GetInt(pControlNode,"urlset",iUrlSet); + + // stuff for button scroller + if ( XMLUtils::GetString(pControlNode, "orientation", strTmp) ) + { + if (strTmp.ToLower() == "horizontal") + { + bHorizontal = true; + orientation = HORIZONTAL; + } + } + GetFloat(pControlNode, "buttongap", buttonGap); + GetFloat(pControlNode, "itemgap", buttonGap); + XMLUtils::GetInt(pControlNode, "numbuttons", iNumSlots); + XMLUtils::GetInt(pControlNode, "movement", iMovementRange); + XMLUtils::GetInt(pControlNode, "defaultbutton", iDefaultSlot); + XMLUtils::GetInt(pControlNode, "alpha", iAlpha); + XMLUtils::GetBoolean(pControlNode, "wraparound", bWrapAround); + XMLUtils::GetBoolean(pControlNode, "smoothscrolling", bSmoothScrolling); + GetAspectRatio(pControlNode, "aspectratio", aspect); + XMLUtils::GetBoolean(pControlNode, "scroll", bScrollLabel); + XMLUtils::GetBoolean(pControlNode,"pulseonselect", bPulse); + + GetInfoTexture(pControlNode, "imagepath", texture, texturePath); + + GetDWORD(pControlNode,"timeperimage", timePerImage); + GetDWORD(pControlNode,"fadetime", fadeTime); + GetDWORD(pControlNode,"pauseatend", timeToPauseAtEnd); + XMLUtils::GetBoolean(pControlNode, "randomize", randomized); + XMLUtils::GetBoolean(pControlNode, "loop", loop); + XMLUtils::GetBoolean(pControlNode, "scrollout", scrollOut); + + GetFloat(pControlNode, "radiowidth", radioWidth); + GetFloat(pControlNode, "radioheight", radioHeight); + GetFloat(pControlNode, "radioposx", radioPosX); + GetFloat(pControlNode, "radioposy", radioPosY); + GetFloat(pControlNode, "spinposx", radioPosX); + CStdString borderStr; + if (XMLUtils::GetString(pControlNode, "bordersize", borderStr)) + GetRectFromString(borderStr, borderSize); + + XMLUtils::GetBoolean(pControlNode, "showonepage", showOnePage); + XMLUtils::GetInt(pControlNode, "focusposition", focusPosition); + XMLUtils::GetInt(pControlNode, "scrolltime", scrollTime); + XMLUtils::GetInt(pControlNode, "preloaditems", preloadItems, 0, 2); + + XMLUtils::GetBoolean(pControlNode, "usecontrolcoords", useControlCoords); + XMLUtils::GetBoolean(pControlNode, "renderfocusedlast", renderFocusedLast); + XMLUtils::GetBoolean(pControlNode, "resetonlabelchange", resetOnLabelChange); + + XMLUtils::GetBoolean(pControlNode, "password", bPassword); + + // view type + VIEW_TYPE viewType = VIEW_TYPE_NONE; + CStdString viewLabel; + if (strType == "panel") + { + viewType = VIEW_TYPE_ICON; + viewLabel = g_localizeStrings.Get(536); + } + else if (strType == "list") + { + viewType = VIEW_TYPE_LIST; + viewLabel = g_localizeStrings.Get(535); + } + else + { + viewType = VIEW_TYPE_WRAP; + viewLabel = g_localizeStrings.Get(541); + } + TiXmlElement *itemElement = pControlNode->FirstChildElement("viewtype"); + if (itemElement && itemElement->FirstChild()) + { + CStdString type = itemElement->FirstChild()->Value(); + if (type == "list") + viewType = VIEW_TYPE_LIST; + else if (type == "icon") + viewType = VIEW_TYPE_ICON; + else if (type == "biglist") + viewType = VIEW_TYPE_BIG_LIST; + else if (type == "bigicon") + viewType = VIEW_TYPE_BIG_ICON; + else if (type == "wide") + viewType = VIEW_TYPE_WIDE; + else if (type == "bigwide") + viewType = VIEW_TYPE_BIG_WIDE; + else if (type == "wrap") + viewType = VIEW_TYPE_WRAP; + else if (type == "bigwrap") + viewType = VIEW_TYPE_BIG_WRAP; + const char *label = itemElement->Attribute("label"); + if (label) + viewLabel = CGUIInfoLabel::GetLabel(FilterLabel(label)); + } + + TiXmlElement *cam = pControlNode->FirstChildElement("camera"); + if (cam) + { + hasCamera = true; + g_SkinInfo.ResolveConstant(cam->Attribute("x"), camera.x); + g_SkinInfo.ResolveConstant(cam->Attribute("y"), camera.y); + } + + XMLUtils::GetInt(pControlNode, "scrollspeed", scrollSpeed); + + ///////////////////////////////////////////////////////////////////////////// + // Instantiate a new control using the properties gathered above + // + + CGUIControl *control = NULL; + if (strType == "group") + { + if (insideContainer) + { + control = new CGUIListGroup(parentID, id, posX, posY, width, height); + } + else + { + control = new CGUIControlGroup( + parentID, id, posX, posY, width, height); + ((CGUIControlGroup *)control)->SetDefaultControl(defaultControl, defaultAlways); + ((CGUIControlGroup *)control)->SetRenderFocusedLast(renderFocusedLast); + } + } + else if (strType == "grouplist") + { + control = new CGUIControlGroupList( + parentID, id, posX, posY, width, height, buttonGap, pageControl, orientation, useControlCoords, labelInfo.align); + ((CGUIControlGroup *)control)->SetRenderFocusedLast(renderFocusedLast); + } + else if (strType == "label") + { + const CGUIInfoLabel &content = (infoLabels.size()) ? infoLabels[0] : CGUIInfoLabel(""); + if (insideContainer) + { // inside lists we use CGUIListLabel + control = new CGUIListLabel(parentID, id, posX, posY, width, height, labelInfo, content, bScrollLabel, scrollSpeed); + } + else + { + control = new CGUILabelControl( + parentID, id, posX, posY, width, height, + labelInfo, wrapMultiLine, bHasPath); + ((CGUILabelControl *)control)->SetInfo(content); + ((CGUILabelControl *)control)->SetWidthControl(bScrollLabel, scrollSpeed); + } + } + else if (strType == "edit") + { + control = new CGUIEditControl( + parentID, id, posX, posY, width, height, textureFocus, textureNoFocus, + labelInfo, strLabel); + + if (bPassword) + ((CGUIEditControl *) control)->SetInputType(CGUIEditControl::INPUT_TYPE_PASSWORD, 0); + ((CGUIEditControl *) control)->SetTextChangeActions(textChangeActions); + } + else if (strType == "videowindow") + { + control = new CGUIVideoControl( + parentID, id, posX, posY, width, height); + } + else if (strType == "fadelabel") + { + control = new CGUIFadeLabelControl( + parentID, id, posX, posY, width, height, + labelInfo, scrollOut, scrollSpeed, timeToPauseAtEnd, resetOnLabelChange); + + ((CGUIFadeLabelControl *)control)->SetInfo(infoLabels); + } + else if (strType == "rss") + { + control = new CGUIRSSControl( + parentID, id, posX, posY, width, height, + labelInfo, textColor3, labelInfo2.textColor, strRSSTags, scrollSpeed); + + std::map<int, std::pair<std::vector<int>,std::vector<string> > >::iterator iter=g_settings.m_mapRssUrls.find(iUrlSet); + if (iter != g_settings.m_mapRssUrls.end()) + { + ((CGUIRSSControl *)control)->SetUrls(iter->second.second); + ((CGUIRSSControl *)control)->SetIntervals(iter->second.first); + } + else + CLog::Log(LOGERROR,"invalid rss url set referenced in skin"); + } + else if (strType == "button") + { + control = new CGUIButtonControl( + parentID, id, posX, posY, width, height, + textureFocus, textureNoFocus, + labelInfo); + + ((CGUIButtonControl *)control)->SetLabel(strLabel); + ((CGUIButtonControl *)control)->SetLabel2(strLabel2); + ((CGUIButtonControl *)control)->SetClickActions(clickActions); + ((CGUIButtonControl *)control)->SetFocusActions(focusActions); + ((CGUIButtonControl *)control)->SetUnFocusActions(unfocusActions); + } + else if (strType == "togglebutton") + { + control = new CGUIToggleButtonControl( + parentID, id, posX, posY, width, height, + textureFocus, textureNoFocus, + textureAltFocus, textureAltNoFocus, labelInfo); + + ((CGUIToggleButtonControl *)control)->SetLabel(strLabel); + ((CGUIToggleButtonControl *)control)->SetAltLabel(altLabel); + ((CGUIToggleButtonControl *)control)->SetClickActions(clickActions); + ((CGUIToggleButtonControl *)control)->SetAltClickActions(altclickActions); + ((CGUIToggleButtonControl *)control)->SetFocusActions(focusActions); + ((CGUIToggleButtonControl *)control)->SetUnFocusActions(unfocusActions); + ((CGUIToggleButtonControl *)control)->SetToggleSelect(iToggleSelect); + } + else if (strType == "checkmark") + { + control = new CGUICheckMarkControl( + parentID, id, posX, posY, width, height, + textureCheckMark, textureCheckMarkNF, + checkWidth, checkHeight, labelInfo); + + ((CGUICheckMarkControl *)control)->SetLabel(strLabel); + } + else if (strType == "radiobutton") + { + control = new CGUIRadioButtonControl( + parentID, id, posX, posY, width, height, + textureFocus, textureNoFocus, + labelInfo, + textureRadioOn, textureRadioOff); + + ((CGUIRadioButtonControl *)control)->SetLabel(strLabel); + ((CGUIRadioButtonControl *)control)->SetRadioDimensions(radioPosX, radioPosY, radioWidth, radioHeight); + ((CGUIRadioButtonControl *)control)->SetToggleSelect(iToggleSelect); + ((CGUIRadioButtonControl *)control)->SetClickActions(clickActions); + ((CGUIRadioButtonControl *)control)->SetFocusActions(focusActions); + ((CGUIRadioButtonControl *)control)->SetUnFocusActions(unfocusActions); + } + else if (strType == "multiselect") + { + CGUIInfoLabel label; + if (infoLabels.size()) + label = infoLabels[0]; + control = new CGUIMultiSelectTextControl( + parentID, id, posX, posY, width, height, + textureFocus, textureNoFocus, labelInfo, label); + } + else if (strType == "spincontrol") + { + control = new CGUISpinControl( + parentID, id, posX, posY, width, height, + textureUp, textureDown, textureUpFocus, textureDownFocus, + labelInfo, iType); + + ((CGUISpinControl *)control)->SetReverse(bReverse); + + if (iType == SPIN_CONTROL_TYPE_INT) + { + ((CGUISpinControl *)control)->SetRange(iMin, iMax); + } + else if (iType == SPIN_CONTROL_TYPE_PAGE) + { + ((CGUISpinControl *)control)->SetRange(iMin, iMax); + ((CGUISpinControl *)control)->SetShowRange(true); + ((CGUISpinControl *)control)->SetReverse(false); + ((CGUISpinControl *)control)->SetShowOnePage(showOnePage); + } + else if (iType == SPIN_CONTROL_TYPE_FLOAT) + { + ((CGUISpinControl *)control)->SetFloatRange(fMin, fMax); + ((CGUISpinControl *)control)->SetFloatInterval(fInterval); + } + } + else if (strType == "slider") + { + control = new CGUISliderControl( + parentID, id, posX, posY, width, height, + textureBar, textureNib, textureNibFocus, SPIN_CONTROL_TYPE_TEXT); + + ((CGUISliderControl *)control)->SetInfo(singleInfo); + } + else if (strType == "sliderex") + { + labelInfo.align |= XBFONT_CENTER_Y; // always center text vertically + control = new CGUISettingsSliderControl( + parentID, id, posX, posY, width, height, sliderWidth, sliderHeight, textureFocus, textureNoFocus, + textureBar, textureNib, textureNibFocus, labelInfo, SPIN_CONTROL_TYPE_TEXT); + + ((CGUISettingsSliderControl *)control)->SetText(strLabel); + ((CGUISettingsSliderControl *)control)->SetInfo(singleInfo); + } + else if (strType == "scrollbar") + { + control = new CGUIScrollBar( + parentID, id, posX, posY, width, height, + textureBackground, textureBar, textureBarFocus, textureNib, textureNibFocus, orientation, showOnePage); + } + else if (strType == "progress") + { + control = new CGUIProgressControl( + parentID, id, posX, posY, width, height, + textureBackground, textureLeft, textureMid, textureRight, + textureOverlay, rMin, rMax, bReveal); + ((CGUIProgressControl *)control)->SetInfo(singleInfo); + } + else if (strType == "image" || strType == "largeimage") + { + if (strType == "largeimage") + texture.useLarge = true; + + // use a bordered texture if we have <bordersize> or <bordertexture> specified. + if (borderTexture.filename.IsEmpty() && borderStr.IsEmpty()) + control = new CGUIImage( + parentID, id, posX, posY, width, height, texture); + else + control = new CGUIBorderedImage( + parentID, id, posX, posY, width, height, texture, borderTexture, borderSize); +#ifdef PRE_SKIN_VERSION_9_10_COMPATIBILITY + if (insideContainer && textureFile.IsConstant()) + aspect.ratio = CAspectRatio::AR_STRETCH; +#endif + ((CGUIImage *)control)->SetInfo(textureFile); + ((CGUIImage *)control)->SetAspectRatio(aspect); + ((CGUIImage *)control)->SetCrossFade(fadeTime); + } + else if (strType == "multiimage") + { + control = new CGUIMultiImage( + parentID, id, posX, posY, width, height, texture, timePerImage, fadeTime, randomized, loop, timeToPauseAtEnd); + ((CGUIMultiImage *)control)->SetInfo(texturePath); + ((CGUIMultiImage *)control)->SetAspectRatio(aspect.ratio); + } + else if (strType == "list") + { + control = new CGUIListContainer(parentID, id, posX, posY, width, height, orientation, scrollTime, preloadItems); + ((CGUIListContainer *)control)->LoadLayout(pControlNode); + ((CGUIListContainer *)control)->LoadContent(pControlNode); + ((CGUIListContainer *)control)->SetType(viewType, viewLabel); + ((CGUIListContainer *)control)->SetPageControl(pageControl); + } + else if (strType == "wraplist") + { + control = new CGUIWrappingListContainer(parentID, id, posX, posY, width, height, orientation, scrollTime, preloadItems, focusPosition); + ((CGUIWrappingListContainer *)control)->LoadLayout(pControlNode); + ((CGUIWrappingListContainer *)control)->LoadContent(pControlNode); + ((CGUIWrappingListContainer *)control)->SetType(viewType, viewLabel); + ((CGUIWrappingListContainer *)control)->SetPageControl(pageControl); + } + else if (strType == "fixedlist") + { + control = new CGUIFixedListContainer(parentID, id, posX, posY, width, height, orientation, scrollTime, preloadItems, focusPosition); + ((CGUIFixedListContainer *)control)->LoadLayout(pControlNode); + ((CGUIFixedListContainer *)control)->LoadContent(pControlNode); + ((CGUIFixedListContainer *)control)->SetType(viewType, viewLabel); + ((CGUIFixedListContainer *)control)->SetPageControl(pageControl); + } + else if (strType == "panel") + { + control = new CGUIPanelContainer(parentID, id, posX, posY, width, height, orientation, scrollTime, preloadItems); + ((CGUIPanelContainer *)control)->LoadLayout(pControlNode); + ((CGUIPanelContainer *)control)->LoadContent(pControlNode); + ((CGUIPanelContainer *)control)->SetType(viewType, viewLabel); + ((CGUIPanelContainer *)control)->SetPageControl(pageControl); + } + else if (strType == "textbox") + { + control = new CGUITextBox( + parentID, id, posX, posY, width, height, + labelInfo, scrollTime); + + ((CGUITextBox *)control)->SetPageControl(pageControl); + if (infoLabels.size()) + ((CGUITextBox *)control)->SetInfo(infoLabels[0]); + ((CGUITextBox *)control)->SetAutoScrolling(pControlNode); + } + else if (strType == "selectbutton") + { + control = new CGUISelectButtonControl( + parentID, id, posX, posY, + width, height, textureFocus, textureNoFocus, + labelInfo, + textureBackground, textureLeft, textureLeftFocus, textureRight, textureRightFocus); + + ((CGUISelectButtonControl *)control)->SetLabel(strLabel); + } + else if (strType == "mover") + { + control = new CGUIMoverControl( + parentID, id, posX, posY, width, height, + textureFocus, textureNoFocus); + } + else if (strType == "resize") + { + control = new CGUIResizeControl( + parentID, id, posX, posY, width, height, + textureFocus, textureNoFocus); + } + else if (strType == "buttonscroller") + { + control = new CGUIButtonScroller( + parentID, id, posX, posY, width, height, buttonGap, iNumSlots, iDefaultSlot, + iMovementRange, bHorizontal, iAlpha, bWrapAround, bSmoothScrolling, + textureFocus, textureNoFocus, labelInfo); + ((CGUIButtonScroller *)control)->LoadButtons(pControlNode); + } + else if (strType == "spincontrolex") + { + control = new CGUISpinControlEx( + parentID, id, posX, posY, width, height, spinWidth, spinHeight, + labelInfo, textureFocus, textureNoFocus, textureUp, textureDown, textureUpFocus, textureDownFocus, + labelInfo, iType); + + ((CGUISpinControlEx *)control)->SetSpinPosition(radioPosX); + ((CGUISpinControlEx *)control)->SetText(strLabel); + ((CGUISpinControlEx *)control)->SetReverse(bReverse); + } + else if (strType == "visualisation" || strType == "karvisualisation") + { + control = new CGUIVisualisationControl(parentID, id, posX, posY, width, height); + } + + // things that apply to all controls + if (control) + { + control->SetHitRect(hitRect); + control->SetVisibleCondition(iVisibleCondition, allowHiddenFocus); + control->SetEnableCondition(enableCondition); + control->SetAnimations(animations); + control->SetColorDiffuse(colorDiffuse); + control->SetNavigation(up, down, left, right); + control->SetTabNavigation(next,prev); + control->SetNavigationActions(upActions, downActions, leftActions, rightActions); + control->SetPulseOnSelect(bPulse); + if (hasCamera) + control->SetCamera(camera); + } + return control; +} + +void CGUIControlFactory::ScaleElement(TiXmlElement *element, RESOLUTION fileRes, RESOLUTION destRes) +{ + if (element->FirstChild()) + { + const char *value = element->FirstChild()->Value(); + if (value) + { + float v = (float)atof(value); + CStdString name = element->Value(); + if (name == "posx" || + name == "width" || + name == "gfxthumbwidth" || + name == "gfxthumbspacex" || + name == "textoffsetx" || + name == "textxoff" || + name == "textxoff2" || + name == "textwidth" || + name == "spinwidth" || + name == "spinposx" || + name == "markwidth" || + name == "sliderwidth" || + name == "itemwidth" || + name == "texturewidth" || + name == "thumbwidth" || + name == "thumbposx" || + name == "thumbwidthbig" || + name == "thumbposxbig" || + name == "texturewidthbig" || + name == "itemwidthbig" || + name == "radiowidth" || + name == "radioposx") + { + // scale + v *= (float)g_settings.m_ResInfo[destRes].iWidth / g_settings.m_ResInfo[fileRes].iWidth; + CStdString floatValue; + floatValue.Format("%f", v); + element->FirstChild()->SetValue(floatValue); + } + else if (name == "posy" || + name == "height" || + name == "textspacey" || + name == "gfxthumbheight" || + name == "gfxthumbspacey" || + name == "textoffsety" || + name == "textyoff" || + name == "textyoff2" || + name == "spinheight" || + name == "spinposy" || + name == "markheight" || + name == "sliderheight" || + name == "spacebetweenitems" || + name == "textureheight" || + name == "thumbheight" || + name == "thumbposy" || + name == "thumbheightbig" || + name == "thumbposybig" || + name == "textureheightbig" || + name == "itemheightbig" || + name == "buttongap" || // should really depend on orientation + name == "radioheight" || + name == "radioposy") + { + // scale + v *= (float)g_settings.m_ResInfo[destRes].iHeight / g_settings.m_ResInfo[fileRes].iHeight; + CStdString floatValue; + floatValue.Format("%f", v); + element->FirstChild()->SetValue(floatValue); + } + } + } +} diff --git a/guilib/GUIControlFactory.h b/guilib/GUIControlFactory.h new file mode 100644 index 0000000000..76db5d3f2f --- /dev/null +++ b/guilib/GUIControlFactory.h @@ -0,0 +1,77 @@ +/*! +\file GuiControlFactory.h +\brief +*/ + +#ifndef GUI_CONTROL_FACTORY_H +#define GIU_CONTROL_FACTORY_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControl.h" + +class CTextureInfo; // forward +class CAspectRatio; +class CGUIInfoLabel; +class TiXmlNode; + +/*! + \ingroup controls + \brief + */ +class CGUIControlFactory +{ +public: + CGUIControlFactory(void); + virtual ~CGUIControlFactory(void); + static CStdString GetType(const TiXmlElement *pControlNode); + CGUIControl* Create(int parentID, const FRECT &rect, TiXmlElement* pControlNode, bool insideContainer = false); + void ScaleElement(TiXmlElement *element, RESOLUTION fileRes, RESOLUTION destRes); + static bool GetFloat(const TiXmlNode* pRootNode, const char* strTag, float& value); + static bool GetDWORD(const TiXmlNode* pRootNode, const char* strTag, DWORD& value); + static bool GetAspectRatio(const TiXmlNode* pRootNode, const char* strTag, CAspectRatio &aspectRatio); + static bool GetInfoTexture(const TiXmlNode* pRootNode, const char* strTag, CTextureInfo &image, CGUIInfoLabel &info); + static bool GetTexture(const TiXmlNode* pRootNode, const char* strTag, CTextureInfo &image); + static bool GetAlignment(const TiXmlNode* pRootNode, const char* strTag, uint32_t& dwAlignment); + static bool GetAlignmentY(const TiXmlNode* pRootNode, const char* strTag, uint32_t& dwAlignment); + static bool GetAnimations(const TiXmlNode *control, const FRECT &rect, std::vector<CAnimation> &animation); + static void GetInfoLabel(const TiXmlNode *pControlNode, const CStdString &labelTag, CGUIInfoLabel &infoLabel); + static void GetInfoLabels(const TiXmlNode *pControlNode, const CStdString &labelTag, std::vector<CGUIInfoLabel> &infoLabels); + static bool GetColor(const TiXmlNode* pRootNode, const char* strTag, color_t &value); + static bool GetInfoColor(const TiXmlNode* pRootNode, const char* strTag, CGUIInfoColor &value); + static CStdString FilterLabel(const CStdString &label); + static bool GetConditionalVisibility(const TiXmlNode* control, int &condition); + static bool GetMultipleString(const TiXmlNode* pRootNode, const char* strTag, std::vector<CGUIActionDescriptor>& vecStringValue); + static void GetRectFromString(const CStdString &string, FRECT &rect); + static bool GetAction(const TiXmlElement* pElement, CGUIActionDescriptor &action); +private: + bool GetNavigation(const TiXmlElement *node, const char *tag, int &direction, std::vector<CGUIActionDescriptor> &actions); + bool GetCondition(const TiXmlNode *control, const char *tag, int &condition); + static bool GetConditionalVisibility(const TiXmlNode* control, int &condition, CGUIInfoBool &allowHiddenFocus); + bool GetString(const TiXmlNode* pRootNode, const char* strTag, CStdString& strString); + bool GetFloatRange(const TiXmlNode* pRootNode, const char* strTag, float& iMinValue, float& iMaxValue, float& iIntervalValue); + bool GetIntRange(const TiXmlNode* pRootNode, const char* strTag, int& iMinValue, int& iMaxValue, int& iIntervalValue); + bool GetHitRect(const TiXmlNode* pRootNode, CRect &rect); +}; +#endif diff --git a/guilib/GUIControlGroup.cpp b/guilib/GUIControlGroup.cpp new file mode 100644 index 0000000000..1b377c1e4d --- /dev/null +++ b/guilib/GUIControlGroup.cpp @@ -0,0 +1,631 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControlGroup.h" +#include "GUIControlProfiler.h" + +using namespace std; + +CGUIControlGroup::CGUIControlGroup() +{ + m_defaultControl = 0; + m_defaultAlways = false; + m_focusedControl = 0; + m_renderTime = 0; + m_renderFocusedLast = false; + ControlType = GUICONTROL_GROUP; +} + +CGUIControlGroup::CGUIControlGroup(int parentID, int controlID, float posX, float posY, float width, float height) +: CGUIControl(parentID, controlID, posX, posY, width, height) +{ + m_defaultControl = 0; + m_defaultAlways = false; + m_focusedControl = 0; + m_renderTime = 0; + m_renderFocusedLast = false; + ControlType = GUICONTROL_GROUP; +} + +CGUIControlGroup::CGUIControlGroup(const CGUIControlGroup &from) +: CGUIControl(from) +{ + m_defaultControl = from.m_defaultControl; + m_defaultAlways = from.m_defaultAlways; + m_renderFocusedLast = from.m_renderFocusedLast; + + // run through and add our controls + for (ciControls it = from.m_children.begin(); it != from.m_children.end(); ++it) + AddControl((*it)->Clone()); + + // defaults + m_focusedControl = 0; + m_renderTime = 0; + ControlType = GUICONTROL_GROUP; +} + +CGUIControlGroup::~CGUIControlGroup(void) +{ + ClearAll(); +} + +void CGUIControlGroup::AllocResources() +{ + CGUIControl::AllocResources(); + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl *control = *it; + if (!control->IsDynamicallyAllocated()) + control->AllocResources(); + } +} + +void CGUIControlGroup::FreeResources() +{ + CGUIControl::FreeResources(); + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl *control = *it; + control->FreeResources(); + } +} + +void CGUIControlGroup::DynamicResourceAlloc(bool bOnOff) +{ + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl *control = *it; + control->DynamicResourceAlloc(bOnOff); + } +} + +void CGUIControlGroup::Render() +{ + g_graphicsContext.SetOrigin(m_posX, m_posY); + CGUIControl *focusedControl = NULL; + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl *control = *it; + GUIPROFILER_VISIBILITY_BEGIN(control); + control->UpdateVisibility(); + GUIPROFILER_VISIBILITY_END(control); + if (m_renderFocusedLast && control->HasFocus()) + focusedControl = control; + else + control->DoRender(m_renderTime); + } + if (focusedControl) + focusedControl->DoRender(m_renderTime); + CGUIControl::Render(); + g_graphicsContext.RestoreOrigin(); +} + +bool CGUIControlGroup::OnAction(const CAction &action) +{ + ASSERT(false); // unimplemented + return false; +} + +bool CGUIControlGroup::HasFocus() const +{ + for (ciControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl *control = *it; + if (control->HasFocus()) + return true; + } + return false; +} + +bool CGUIControlGroup::OnMessage(CGUIMessage& message) +{ + switch (message.GetMessage() ) + { + case GUI_MSG_ITEM_SELECT: + { + if (message.GetControlId() == GetID()) + { + m_focusedControl = message.GetParam1(); + return true; + } + break; + } + case GUI_MSG_ITEM_SELECTED: + { + if (message.GetControlId() == GetID()) + { + message.SetParam1(m_focusedControl); + return true; + } + break; + } + case GUI_MSG_FOCUSED: + { // a control has been focused + m_focusedControl = message.GetControlId(); + SetFocus(true); + // tell our parent thatwe have focus + if (m_parentControl) + m_parentControl->OnMessage(message); + return true; + } + case GUI_MSG_SETFOCUS: + { + // first try our last focused control... + if (!m_defaultAlways && m_focusedControl) + { + CGUIControl *control = GetFirstFocusableControl(m_focusedControl); + if (control) + { + CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), control->GetID()); + return control->OnMessage(msg); + } + } + // ok, no previously focused control, try the default control first + if (m_defaultControl) + { + CGUIControl *control = GetFirstFocusableControl(m_defaultControl); + if (control) + { + CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), control->GetID()); + return control->OnMessage(msg); + } + } + // no success with the default control, so just find one to focus + CGUIControl *control = GetFirstFocusableControl(0); + if (control) + { + CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), control->GetID()); + return control->OnMessage(msg); + } + // unsuccessful + return false; + break; + } + case GUI_MSG_LOSTFOCUS: + { + // set all subcontrols unfocused + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + (*it)->SetFocus(false); + if (!HasID(message.GetParam1())) + { // we don't have the new id, so unfocus + SetFocus(false); + if (m_parentControl) + m_parentControl->OnMessage(message); + } + return true; + } + break; + case GUI_MSG_PAGE_CHANGE: + case GUI_MSG_REFRESH_THUMBS: + case GUI_MSG_REFRESH_LIST: + { // send to all child controls (make sure the target is the control id) + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIMessage msg(message.GetMessage(), message.GetSenderId(), (*it)->GetID(), message.GetParam1()); + (*it)->OnMessage(msg); + } + return true; + } + break; + } + bool handled(false); + //not intented for any specific control, send to all childs and our base handler. + if (message.GetControlId() == 0) + { + for (iControls it = m_children.begin();it != m_children.end(); ++it) + { + CGUIControl* control = *it; + handled |= control->OnMessage(message); + } + return CGUIControl::OnMessage(message) || handled; + } + // if it's intended for us, then so be it + if (message.GetControlId() == GetID()) + return CGUIControl::OnMessage(message); + + return SendControlMessage(message); +} + +bool CGUIControlGroup::SendControlMessage(CGUIMessage &message) +{ + // see if a child matches, and send to the child control if so + for (iControls it = m_children.begin();it != m_children.end(); ++it) + { + CGUIControl* control = *it; + if (control->HasVisibleID(message.GetControlId())) + { + if (control->OnMessage(message)) + return true; + } + } + // Unhandled - send to all matching invisible controls as well + bool handled(false); + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl* control = *it; + if (control->HasID(message.GetControlId())) + { + if (control->OnMessage(message)) + handled = true; + } + } + return handled; +} + +bool CGUIControlGroup::CanFocus() const +{ + if (!CGUIControl::CanFocus()) return false; + // see if we have any children that can be focused + for (ciControls it = m_children.begin(); it != m_children.end(); ++it) + { + if ((*it)->CanFocus()) + return true; + } + return false; +} + +void CGUIControlGroup::DoRender(DWORD currentTime) +{ + m_renderTime = currentTime; + CGUIControl::DoRender(currentTime); +} + +void CGUIControlGroup::SetInitialVisibility() +{ + CGUIControl::SetInitialVisibility(); + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + (*it)->SetInitialVisibility(); +} + +void CGUIControlGroup::QueueAnimation(ANIMATION_TYPE animType) +{ + CGUIControl::QueueAnimation(animType); + // send window level animations to our children as well + if (animType == ANIM_TYPE_WINDOW_OPEN || animType == ANIM_TYPE_WINDOW_CLOSE) + { + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + (*it)->QueueAnimation(animType); + } +} + +void CGUIControlGroup::ResetAnimation(ANIMATION_TYPE animType) +{ + CGUIControl::ResetAnimation(animType); + // send window level animations to our children as well + if (animType == ANIM_TYPE_WINDOW_OPEN || animType == ANIM_TYPE_WINDOW_CLOSE) + { + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + (*it)->ResetAnimation(animType); + } +} + +void CGUIControlGroup::ResetAnimations() +{ // resets all animations, regardless of condition + CGUIControl::ResetAnimations(); + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + (*it)->ResetAnimations(); +} + +bool CGUIControlGroup::IsAnimating(ANIMATION_TYPE animType) +{ + if (CGUIControl::IsAnimating(animType)) + return true; + + if (IsVisible()) + { + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + if ((*it)->IsAnimating(animType)) + return true; + } + } + return false; +} + +bool CGUIControlGroup::HasAnimation(ANIMATION_TYPE animType) +{ + if (CGUIControl::HasAnimation(animType)) + return true; + + if (IsVisible()) + { + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + if ((*it)->HasAnimation(animType)) + return true; + } + } + return false; +} + +bool CGUIControlGroup::HitTest(const CPoint &point) const +{ + for (ciControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl *child = *it; + if (child->HitTest(point - CPoint(m_posX, m_posX))) + return true; + } + return false; +} + +bool CGUIControlGroup::CanFocusFromPoint(const CPoint &point, CGUIControl **control, CPoint &controlPoint) const +{ + if (!CGUIControl::CanFocus()) return false; + CPoint controlCoords(point); + m_transform.InverseTransformPosition(controlCoords.x, controlCoords.y); + for (crControls it = m_children.rbegin(); it != m_children.rend(); ++it) + { + CGUIControl *child = *it; + if (child->CanFocusFromPoint(controlCoords - CPoint(m_posX, m_posY), control, controlPoint)) + return true; + } + *control = NULL; + return false; +} + +void CGUIControlGroup::UnfocusFromPoint(const CPoint &point) +{ + CPoint controlCoords(point); + m_transform.InverseTransformPosition(controlCoords.x, controlCoords.y); + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl *child = *it; + child->UnfocusFromPoint(controlCoords - CPoint(m_posX, m_posY)); + } + CGUIControl::UnfocusFromPoint(point); +} + +bool CGUIControlGroup::HasID(int id) const +{ + if (CGUIControl::HasID(id)) return true; + for (ciControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl *child = *it; + if (child->HasID(id)) + return true; + } + return false; +} + +bool CGUIControlGroup::HasVisibleID(int id) const +{ + // call base class first as the group may be the requested control + if (CGUIControl::HasVisibleID(id)) return true; + // if the group isn't visible, then none of it's children can be + if (!IsVisible()) return false; + for (ciControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl *child = *it; + if (child->HasVisibleID(id)) + return true; + } + return false; +} + +const CGUIControl* CGUIControlGroup::GetControl(int iControl) const +{ + CGUIControl *pPotential = NULL; + LookupMap::const_iterator first = m_lookup.find(iControl); + if (first != m_lookup.end()) + { + LookupMap::const_iterator last = m_lookup.upper_bound(iControl); + for (LookupMap::const_iterator i = first; i != last; i++) + { + CGUIControl *control = i->second; + if (control->IsVisible()) + return control; + else if (!pPotential) + pPotential = control; + } + } + return pPotential; +} + +int CGUIControlGroup::GetFocusedControlID() const +{ + if (m_focusedControl) return m_focusedControl; + CGUIControl *control = GetFocusedControl(); + if (control) return control->GetID(); + return 0; +} + +CGUIControl *CGUIControlGroup::GetFocusedControl() const +{ + for (ciControls it = m_children.begin(); it != m_children.end(); ++it) + { + const CGUIControl* control = *it; + if (control->HasFocus()) + { + if (control->IsGroup()) + { + CGUIControlGroup *group = (CGUIControlGroup *)control; + return group->GetFocusedControl(); + } + return (CGUIControl *)control; + } + } + return NULL; +} + +// in the case of id == 0, we don't match id +CGUIControl *CGUIControlGroup::GetFirstFocusableControl(int id) +{ + if (!CanFocus()) return NULL; + if (id && id == (int) GetID()) return this; // we're focusable and they want us + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl* pControl = *it; + if (pControl->IsGroup()) + { + CGUIControlGroup *group = (CGUIControlGroup *)pControl; + CGUIControl *control = group->GetFirstFocusableControl(id); + if (control) return control; + } + if ((!id || (int) pControl->GetID() == id) && pControl->CanFocus()) + return pControl; + } + return NULL; +} + +void CGUIControlGroup::AddControl(CGUIControl *control, int position /* = -1*/) +{ + if (!control) return; + if (position < 0 || position > (int)m_children.size()) + position = (int)m_children.size(); + m_children.insert(m_children.begin() + position, control); + control->SetParentControl(this); + AddLookup(control); +} + +void CGUIControlGroup::AddLookup(CGUIControl *control) +{ + if (control->IsGroup()) + { // first add all the subitems of this group (if they exist) + const LookupMap map = ((CGUIControlGroup *)control)->GetLookup(); + for (LookupMap::const_iterator i = map.begin(); i != map.end(); i++) + m_lookup.insert(m_lookup.upper_bound(i->first), make_pair(i->first, i->second)); + } + if (control->GetID()) + m_lookup.insert(m_lookup.upper_bound(control->GetID()), make_pair(control->GetID(), control)); + // ensure that our size is what it should be + if (m_parentControl) + ((CGUIControlGroup *)m_parentControl)->AddLookup(control); +} + +void CGUIControlGroup::RemoveLookup(CGUIControl *control) +{ + if (control->IsGroup()) + { // remove the group's lookup + const LookupMap &map = ((CGUIControlGroup *)control)->GetLookup(); + for (LookupMap::const_iterator i = map.begin(); i != map.end(); i++) + { // remove this control + for (LookupMap::iterator it = m_lookup.begin(); it != m_lookup.end(); it++) + { + if (i->second == it->second) + { + m_lookup.erase(it); + break; + } + } + } + } + // remove the actual control + if (control->GetID()) + { + for (LookupMap::iterator it = m_lookup.begin(); it != m_lookup.end(); it++) + { + if (control == it->second) + { + m_lookup.erase(it); + break; + } + } + } + if (m_parentControl) + ((CGUIControlGroup *)m_parentControl)->RemoveLookup(control); +} + +bool CGUIControlGroup::InsertControl(CGUIControl *control, const CGUIControl *insertPoint) +{ + // find our position + for (unsigned int i = 0; i < m_children.size(); i++) + { + CGUIControl *child = m_children[i]; + if (child->IsGroup() && ((CGUIControlGroup *)child)->InsertControl(control, insertPoint)) + return true; + else if (child == insertPoint) + { + AddControl(control, i); + return true; + } + } + return false; +} + +void CGUIControlGroup::SaveStates(vector<CControlState> &states) +{ + // save our state, and that of our children + states.push_back(CControlState(GetID(), m_focusedControl)); + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + (*it)->SaveStates(states); +} + +// Note: This routine doesn't delete the control. It just removes it from the control list +bool CGUIControlGroup::RemoveControl(const CGUIControl *control) +{ + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl *child = *it; + if (child->IsGroup() && ((CGUIControlGroup *)child)->RemoveControl(control)) + return true; + if (control == child) + { + m_children.erase(it); + RemoveLookup(child); + return true; + } + } + return false; +} + +void CGUIControlGroup::ClearAll() +{ + // first remove from the lookup table + if (m_parentControl) + { + for (iControls it = m_children.begin(); it != m_children.end(); it++) + ((CGUIControlGroup *)m_parentControl)->RemoveLookup(*it); + } + // and delete all our children + for (iControls it = m_children.begin(); it != m_children.end(); it++) + { + CGUIControl *control = *it; + delete control; + } + m_children.clear(); + m_lookup.clear(); +} + +void CGUIControlGroup::GetContainers(vector<CGUIControl *> &containers) const +{ + for (ciControls it = m_children.begin();it != m_children.end(); ++it) + { + if ((*it)->IsContainer()) + containers.push_back(*it); + else if ((*it)->IsGroup()) + ((CGUIControlGroup *)(*it))->GetContainers(containers); + } +} + +void CGUIControlGroup::SetInvalid() +{ + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + (*it)->SetInvalid(); +} + +#ifdef _DEBUG +void CGUIControlGroup::DumpTextureUse() +{ + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + (*it)->DumpTextureUse(); +} +#endif diff --git a/guilib/GUIControlGroup.h b/guilib/GUIControlGroup.h new file mode 100644 index 0000000000..914a9f84ce --- /dev/null +++ b/guilib/GUIControlGroup.h @@ -0,0 +1,113 @@ +/*! +\file GUIControlGroup.h +\brief +*/ + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControl.h" + +/*! + \ingroup controls + \brief group of controls, useful for remembering last control + animating/hiding together + */ +class CGUIControlGroup : public CGUIControl +{ +public: + CGUIControlGroup(); + CGUIControlGroup(int parentID, int controlID, float posX, float posY, float width, float height); + CGUIControlGroup(const CGUIControlGroup &from); + virtual ~CGUIControlGroup(void); + virtual CGUIControlGroup *Clone() const { return new CGUIControlGroup(*this); }; + + virtual void Render(); + virtual bool OnAction(const CAction &action); + virtual bool OnMessage(CGUIMessage& message); + virtual bool SendControlMessage(CGUIMessage& message); + virtual bool HasFocus() const; + virtual void AllocResources(); + virtual void FreeResources(); + virtual void DynamicResourceAlloc(bool bOnOff); + virtual bool CanFocus() const; + + virtual bool HitTest(const CPoint &point) const; + virtual bool CanFocusFromPoint(const CPoint &point, CGUIControl **control, CPoint &controlPoint) const; + virtual void UnfocusFromPoint(const CPoint &point); + + virtual void SetInitialVisibility(); + + virtual void DoRender(DWORD currentTime); + virtual bool IsAnimating(ANIMATION_TYPE anim); + virtual bool HasAnimation(ANIMATION_TYPE anim); + virtual void QueueAnimation(ANIMATION_TYPE anim); + virtual void ResetAnimation(ANIMATION_TYPE anim); + virtual void ResetAnimations(); + + virtual bool HasID(int id) const; + virtual bool HasVisibleID(int id) const; + virtual void SetInvalid(); + + int GetFocusedControlID() const; + CGUIControl *GetFocusedControl() const; + const CGUIControl *GetControl(int id) const; + virtual CGUIControl *GetFirstFocusableControl(int id); + void GetContainers(std::vector<CGUIControl *> &containers) const; + + virtual void AddControl(CGUIControl *control, int position = -1); + bool InsertControl(CGUIControl *control, const CGUIControl *insertPoint); + virtual bool RemoveControl(const CGUIControl *control); + virtual void ClearAll(); + void SetDefaultControl(int id, bool always) { m_defaultControl = id; m_defaultAlways = always; }; + void SetRenderFocusedLast(bool renderLast) { m_renderFocusedLast = renderLast; }; + + virtual void SaveStates(std::vector<CControlState> &states); + + virtual bool IsGroup() const { return true; }; + +#ifdef _DEBUG + virtual void DumpTextureUse(); +#endif +protected: + // sub controls + std::vector<CGUIControl *> m_children; + typedef std::vector<CGUIControl *>::iterator iControls; + typedef std::vector<CGUIControl *>::const_iterator ciControls; + typedef std::vector<CGUIControl *>::const_reverse_iterator crControls; + + // fast lookup by id + typedef std::multimap<int, CGUIControl *> LookupMap; + void AddLookup(CGUIControl *control); + void RemoveLookup(CGUIControl *control); + const LookupMap &GetLookup() { return m_lookup; }; + LookupMap m_lookup; + + int m_defaultControl; + bool m_defaultAlways; + int m_focusedControl; + bool m_renderFocusedLast; + + // render time + DWORD m_renderTime; +}; + diff --git a/guilib/GUIControlGroupList.cpp b/guilib/GUIControlGroupList.cpp new file mode 100644 index 0000000000..02c093f6ac --- /dev/null +++ b/guilib/GUIControlGroupList.cpp @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControlGroupList.h" +#include "utils/GUIInfoManager.h" +#include "GUIControlProfiler.h" + +#define TIME_TO_SCROLL 200; + +CGUIControlGroupList::CGUIControlGroupList(int parentID, int controlID, float posX, float posY, float width, float height, float itemGap, int pageControl, ORIENTATION orientation, bool useControlPositions, uint32_t alignment) +: CGUIControlGroup(parentID, controlID, posX, posY, width, height) +{ + m_itemGap = itemGap; + m_pageControl = pageControl; + m_offset = 0; + m_totalSize = 10; + m_orientation = orientation; + m_alignment = alignment; + m_scrollOffset = 0; + m_scrollSpeed = 0; + m_scrollTime = 0; + m_renderTime = 0; + m_useControlPositions = useControlPositions; + ControlType = GUICONTROL_GROUPLIST; +} + +CGUIControlGroupList::~CGUIControlGroupList(void) +{ +} + +void CGUIControlGroupList::Render() +{ + if (m_scrollSpeed != 0) + { + m_offset += m_scrollSpeed * (m_renderTime - m_scrollTime); + if ((m_scrollSpeed < 0 && m_offset < m_scrollOffset) || + (m_scrollSpeed > 0 && m_offset > m_scrollOffset)) + { + m_offset = m_scrollOffset; + m_scrollSpeed = 0; + } + } + m_scrollTime = m_renderTime; + + // first we update visibility of all our items, to ensure our size and + // alignment computations are correct. + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl *control = *it; + GUIPROFILER_VISIBILITY_BEGIN(control); + control->UpdateVisibility(); + GUIPROFILER_VISIBILITY_END(control); + } + + ValidateOffset(); + if (m_pageControl) + { + CGUIMessage message(GUI_MSG_LABEL_RESET, GetParentID(), m_pageControl, (int)m_height, (int)m_totalSize); + SendWindowMessage(message); + CGUIMessage message2(GUI_MSG_ITEM_SELECT, GetParentID(), m_pageControl, (int)m_offset); + SendWindowMessage(message2); + } + // we run through the controls, rendering as we go + bool render(g_graphicsContext.SetClipRegion(m_posX, m_posY, m_width, m_height)); + float pos = GetAlignOffset(); + float focusedPos = 0; + CGUIControl *focusedControl = NULL; + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + // note we render all controls, even if they're offscreen, as then they'll be updated + // with respect to animations + CGUIControl *control = *it; + if (m_renderFocusedLast && control->HasFocus()) + { + focusedControl = control; + focusedPos = pos; + } + else + { + if (m_orientation == VERTICAL) + g_graphicsContext.SetOrigin(m_posX, m_posY + pos - m_offset); + else + g_graphicsContext.SetOrigin(m_posX + pos - m_offset, m_posY); + control->DoRender(m_renderTime); + } + if (control->IsVisible()) + pos += Size(control) + m_itemGap; + g_graphicsContext.RestoreOrigin(); + } + if (focusedControl) + { + if (m_orientation == VERTICAL) + g_graphicsContext.SetOrigin(m_posX, m_posY + focusedPos - m_offset); + else + g_graphicsContext.SetOrigin(m_posX + focusedPos - m_offset, m_posY); + focusedControl->DoRender(m_renderTime); + } + if (render) g_graphicsContext.RestoreClipRegion(); + CGUIControl::Render(); +} + +bool CGUIControlGroupList::OnMessage(CGUIMessage& message) +{ + switch (message.GetMessage() ) + { + case GUI_MSG_FOCUSED: + { // a control has been focused + // scroll if we need to and update our page control + ValidateOffset(); + float offset = 0; + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl *control = *it; + if (!control->IsVisible()) + continue; + if (control->HasID(message.GetControlId())) + { + // find out whether this is the first or last control + if (IsFirstFocusableControl(control)) + ScrollTo(0); + else if (IsLastFocusableControl(control)) + ScrollTo(m_totalSize - Size()); + else if (offset < m_offset) + ScrollTo(offset); + else if (offset + Size(control) > m_offset + Size()) + ScrollTo(offset + Size(control) - Size()); + break; + } + offset += Size(control) + m_itemGap; + } + } + break; + case GUI_MSG_SETFOCUS: + { + // we've been asked to focus. We focus the last control if it's on this page, + // else we'll focus the first focusable control from our offset (after verifying it) + ValidateOffset(); + // now check the focusControl's offset + float offset = 0; + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl *control = *it; + if (!control->IsVisible()) + continue; + if (control->HasID(m_focusedControl)) + { + if (offset >= m_offset && offset + Size(control) <= m_offset + Size()) + return CGUIControlGroup::OnMessage(message); + break; + } + offset += Size(control) + m_itemGap; + } + // find the first control on this page + offset = 0; + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl *control = *it; + if (!control->IsVisible()) + continue; + if (control->CanFocus() && offset >= m_offset && offset + Size(control) <= m_offset + Size()) + { + m_focusedControl = control->GetID(); + break; + } + offset += Size(control) + m_itemGap; + } + } + break; + case GUI_MSG_PAGE_CHANGE: + { + if (message.GetSenderId() == m_pageControl) + { // it's from our page control + ScrollTo((float)message.GetParam1()); + return true; + } + } + break; + } + return CGUIControlGroup::OnMessage(message); +} + +void CGUIControlGroupList::ValidateOffset() +{ + // calculate how many items we have on this page + m_totalSize = 0; + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl *control = *it; + if (!control->IsVisible()) continue; + m_totalSize += Size(control) + m_itemGap; + } + if (m_totalSize > 0) m_totalSize -= m_itemGap; + // check our m_offset range + if (m_offset > m_totalSize - Size()) + m_offset = m_totalSize - Size(); + if (m_offset < 0) m_offset = 0; +} + +void CGUIControlGroupList::AddControl(CGUIControl *control, int position /*= -1*/) +{ + // NOTE: We override control navigation here, but we don't override the <onleft> etc. builtins + // if specified. + if (position < 0 || position > (int)m_children.size()) // add at the end + position = (int)m_children.size(); + + if (control) + { // set the navigation of items so that they form a list + int beforeID = (m_orientation == VERTICAL) ? GetControlIdUp() : GetControlIdLeft(); + int afterID = (m_orientation == VERTICAL) ? GetControlIdDown() : GetControlIdRight(); + if (m_children.size()) + { + // we're inserting at the given position, so grab the items above and below and alter + // their navigation accordingly + CGUIControl *before = NULL; + CGUIControl *after = NULL; + if (position == 0) + { // inserting at the beginning + after = m_children[0]; + if (afterID == GetID()) // we're wrapping around bottom->top, so we have to update the last item + before = m_children[m_children.size() - 1]; + if (beforeID == GetID()) // we're wrapping around top->bottom + beforeID = m_children[m_children.size() - 1]->GetID(); + afterID = after->GetID(); + } + else if (position == (int)m_children.size()) + { // inserting at the end + before = m_children[m_children.size() - 1]; + if (beforeID == GetID()) // we're wrapping around top->bottom, so we have to update the first item + after = m_children[0]; + if (afterID == GetID()) // we're wrapping around bottom->top + afterID = m_children[0]->GetID(); + beforeID = before->GetID(); + } + else + { // inserting somewhere in the middle + before = m_children[position - 1]; + after = m_children[position]; + beforeID = before->GetID(); + afterID = after->GetID(); + } + if (m_orientation == VERTICAL) + { + if (before) // update the DOWN action to point to us + before->SetNavigation(before->GetControlIdUp(), control->GetID(), GetControlIdLeft(), GetControlIdRight()); + if (after) // update the UP action to point to us + after->SetNavigation(control->GetID(), after->GetControlIdDown(), GetControlIdLeft(), GetControlIdRight()); + } + else + { + if (before) // update the RIGHT action to point to us + before->SetNavigation(GetControlIdUp(), GetControlIdDown(), before->GetControlIdLeft(), control->GetID()); + if (after) // update the LEFT action to point to us + after->SetNavigation(GetControlIdUp(), GetControlIdDown(), control->GetID(), after->GetControlIdRight()); + } + } + // now the control's nav + if (m_orientation == VERTICAL) + control->SetNavigation(beforeID, afterID, GetControlIdLeft(), GetControlIdRight()); + else + control->SetNavigation(GetControlIdUp(), GetControlIdDown(), beforeID, afterID); + + if (!m_useControlPositions) + control->SetPosition(0,0); + CGUIControlGroup::AddControl(control, position); + } +} + +void CGUIControlGroupList::ClearAll() +{ + CGUIControlGroup::ClearAll(); + m_offset = 0; +} + +inline float CGUIControlGroupList::Size(const CGUIControl *control) const +{ + return (m_orientation == VERTICAL) ? control->GetYPosition() + control->GetHeight() : control->GetXPosition() + control->GetWidth(); +} + +inline float CGUIControlGroupList::Size() const +{ + return (m_orientation == VERTICAL) ? m_height : m_width; +} + +void CGUIControlGroupList::ScrollTo(float offset) +{ + m_scrollOffset = offset; + m_scrollSpeed = (m_scrollOffset - m_offset) / TIME_TO_SCROLL; +} + +bool CGUIControlGroupList::CanFocusFromPoint(const CPoint &point, CGUIControl **control, CPoint &controlPoint) const +{ + if (!CGUIControl::CanFocus()) return false; + float pos = 0; + CPoint controlCoords(point); + m_transform.InverseTransformPosition(controlCoords.x, controlCoords.y); + float alignOffset = GetAlignOffset(); + for (ciControls it = m_children.begin(); it != m_children.end(); ++it) + { + const CGUIControl *child = *it; + if (child->IsVisible()) + { + if (pos + Size(child) > m_offset && pos < m_offset + Size()) + { // we're on screen + float offsetX = m_orientation == VERTICAL ? m_posX : m_posX + alignOffset + pos - m_offset; + float offsetY = m_orientation == VERTICAL ? m_posY + alignOffset + pos - m_offset : m_posY; + if (child->CanFocusFromPoint(controlCoords - CPoint(offsetX, offsetY), control, controlPoint)) + return true; + } + pos += Size(child) + m_itemGap; + } + } + *control = NULL; + return false; +} + +void CGUIControlGroupList::UnfocusFromPoint(const CPoint &point) +{ + float pos = 0; + CPoint controlCoords(point); + m_transform.InverseTransformPosition(controlCoords.x, controlCoords.y); + float alignOffset = GetAlignOffset(); + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl *child = *it; + if (child->IsVisible()) + { + if (pos + Size(child) > m_offset && pos < m_offset + Size()) + { // we're on screen + CPoint offset = (m_orientation == VERTICAL) ? CPoint(m_posX, m_posY + alignOffset + pos - m_offset) : CPoint(m_posX + alignOffset + pos - m_offset, m_posY); + child->UnfocusFromPoint(controlCoords - offset); + } + pos += Size(child) + m_itemGap; + } + } + CGUIControl::UnfocusFromPoint(point); +} + +bool CGUIControlGroupList::GetCondition(int condition, int data) const +{ + switch (condition) + { + case CONTAINER_HAS_NEXT: + return (m_totalSize >= Size() && m_offset < m_totalSize - Size()); + case CONTAINER_HAS_PREVIOUS: + return (m_offset > 0); + default: + return false; + } +} + +bool CGUIControlGroupList::IsFirstFocusableControl(const CGUIControl *control) const +{ + for (ciControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl *child = *it; + if (child->IsVisible() && child->CanFocus()) + { // found first focusable + return child == control; + } + } + return false; +} + +bool CGUIControlGroupList::IsLastFocusableControl(const CGUIControl *control) const +{ + for (crControls it = m_children.rbegin(); it != m_children.rend(); ++it) + { + CGUIControl *child = *it; + if (child->IsVisible() && child->CanFocus()) + { // found first focusable + return child == control; + } + } + return false; +} + +float CGUIControlGroupList::GetAlignOffset() const +{ + if (m_totalSize < Size()) + { + if (m_alignment & XBFONT_RIGHT) + return Size() - m_totalSize; + if (m_alignment & XBFONT_CENTER_X) + return (Size() - m_totalSize)*0.5f; + } + return 0.0f; +} diff --git a/guilib/GUIControlGroupList.h b/guilib/GUIControlGroupList.h new file mode 100644 index 0000000000..e378a3adc0 --- /dev/null +++ b/guilib/GUIControlGroupList.h @@ -0,0 +1,74 @@ +/*! +\file GUIControlGroupList.h +\brief +*/ + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControlGroup.h" + +/*! + \ingroup controls + \brief list of controls that is scrollable + */ +class CGUIControlGroupList : public CGUIControlGroup +{ +public: + CGUIControlGroupList(int parentID, int controlID, float posX, float posY, float width, float height, float itemGap, int pageControl, ORIENTATION orientation, bool useControlPositions, uint32_t alignment); + virtual ~CGUIControlGroupList(void); + virtual CGUIControlGroupList *Clone() const { return new CGUIControlGroupList(*this); }; + + virtual void Render(); + virtual bool OnMessage(CGUIMessage& message); + virtual bool CanFocusFromPoint(const CPoint &point, CGUIControl **control, CPoint &controlPoint) const; + virtual void UnfocusFromPoint(const CPoint &point); + + virtual void AddControl(CGUIControl *control, int position = -1); + virtual void ClearAll(); + + virtual bool GetCondition(int condition, int data) const; +protected: + bool IsFirstFocusableControl(const CGUIControl *control) const; + bool IsLastFocusableControl(const CGUIControl *control) const; + void ValidateOffset(); + inline float Size(const CGUIControl *control) const; + inline float Size() const; + void ScrollTo(float offset); + float GetAlignOffset() const; + + float m_itemGap; + int m_pageControl; + + float m_offset; // measurement in pixels of our origin + float m_totalSize; + + float m_scrollSpeed; + float m_scrollOffset; + DWORD m_scrollTime; + + bool m_useControlPositions; + ORIENTATION m_orientation; + uint32_t m_alignment; +}; + diff --git a/guilib/GUIControlProfiler.cpp b/guilib/GUIControlProfiler.cpp new file mode 100644 index 0000000000..f7071b088f --- /dev/null +++ b/guilib/GUIControlProfiler.cpp @@ -0,0 +1,368 @@ +/*
+ * Copyright (C) 2005-2009 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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, 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 XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIControlProfiler.h"
+#include "tinyXML/tinyxml.h"
+
+bool CGUIControlProfiler::m_bIsRunning = false;
+
+CGUIControlProfilerItem::CGUIControlProfilerItem(CGUIControlProfiler *pProfiler, CGUIControlProfilerItem *pParent, CGUIControl *pControl)
+: m_pProfiler(pProfiler), m_pParent(pParent), m_pControl(pControl), m_dwVisTime(0), m_dwRenderTime(0)
+{
+ if (m_pControl)
+ {
+ m_controlID = m_pControl->GetID();
+ m_ControlType = m_pControl->GetControlType();
+ m_strDescription = m_pControl->GetDescription();
+ }
+ else
+ {
+ m_controlID = 0;
+ m_ControlType = CGUIControl::GUICONTROL_UNKNOWN;
+ }
+}
+
+CGUIControlProfilerItem::~CGUIControlProfilerItem(void)
+{
+ Reset(NULL);
+}
+
+void CGUIControlProfilerItem::Reset(CGUIControlProfiler *pProfiler)
+{
+ m_controlID = 0;
+ m_ControlType = CGUIControl::GUICONTROL_UNKNOWN;
+ m_pControl = NULL;
+
+ m_dwVisTime = 0;
+ m_dwRenderTime = 0;
+ const unsigned int dwSize = m_vecChildren.size();
+ for (unsigned int i=0; i<dwSize; ++i)
+ delete m_vecChildren[i];
+ m_vecChildren.clear();
+
+ m_pProfiler = pProfiler;
+}
+
+void CGUIControlProfilerItem::BeginVisibility(void)
+{
+ QueryPerformanceCounter(&m_i64VisStart);
+}
+
+void CGUIControlProfilerItem::EndVisibility(void)
+{
+ LARGE_INTEGER t2;
+ QueryPerformanceCounter(&t2);
+ m_dwVisTime += (DWORD)(m_pProfiler->m_fPerfScale * (t2.QuadPart - m_i64VisStart.QuadPart));
+}
+
+void CGUIControlProfilerItem::BeginRender(void)
+{
+ QueryPerformanceCounter(&m_i64RenderStart);
+}
+
+void CGUIControlProfilerItem::EndRender(void)
+{
+ LARGE_INTEGER t2;
+ QueryPerformanceCounter(&t2);
+ m_dwRenderTime += (DWORD)(m_pProfiler->m_fPerfScale * (t2.QuadPart - m_i64RenderStart.QuadPart));
+}
+
+void CGUIControlProfilerItem::SaveToXML(TiXmlElement *parent)
+{
+ TiXmlElement *xmlControl = new TiXmlElement("control");
+ parent->LinkEndChild(xmlControl);
+
+ const char *lpszType = NULL;
+ switch (m_ControlType)
+ {
+ case CGUIControl::GUICONTROL_BUTTON:
+ lpszType = "button"; break;
+ case CGUIControl::GUICONTROL_CHECKMARK:
+ lpszType = "checkmark"; break;
+ case CGUIControl::GUICONTROL_FADELABEL:
+ lpszType = "fadelabel"; break;
+ case CGUIControl::GUICONTROL_IMAGE:
+ case CGUIControl::GUICONTROL_BORDEREDIMAGE:
+ lpszType = "image"; break;
+ case CGUIControl::GUICONTROL_LARGE_IMAGE:
+ lpszType = "largeimage"; break;
+ case CGUIControl::GUICONTROL_LABEL:
+ lpszType = "label"; break;
+ case CGUIControl::GUICONTROL_LISTGROUP:
+ lpszType = "group"; break;
+ case CGUIControl::GUICONTROL_PROGRESS:
+ lpszType = "progress"; break;
+ case CGUIControl::GUICONTROL_RADIO:
+ lpszType = "radiobutton"; break;
+ case CGUIControl::GUICONTROL_RSS:
+ lpszType = "rss"; break;
+ case CGUIControl::GUICONTROL_SELECTBUTTON:
+ lpszType = "selectbutton"; break;
+ case CGUIControl::GUICONTROL_SLIDER:
+ lpszType = "slider"; break;
+ case CGUIControl::GUICONTROL_SETTINGS_SLIDER:
+ lpszType = "sliderex"; break;
+ case CGUIControl::GUICONTROL_SPIN:
+ lpszType = "spincontrol"; break;
+ case CGUIControl::GUICONTROL_SPINEX:
+ lpszType = "spincontrolex"; break;
+ case CGUIControl::GUICONTROL_TEXTBOX:
+ lpszType = "textbox"; break;
+ case CGUIControl::GUICONTROL_TOGGLEBUTTON:
+ lpszType = "togglebutton"; break;
+ case CGUIControl::GUICONTROL_VIDEO:
+ lpszType = "videowindow"; break;
+ case CGUIControl::GUICONTROL_MOVER:
+ lpszType = "mover"; break;
+ case CGUIControl::GUICONTROL_RESIZE:
+ lpszType = "resize"; break;
+ case CGUIControl::GUICONTROL_BUTTONBAR:
+ lpszType = "buttonscroller"; break;
+ case CGUIControl::GUICONTROL_EDIT:
+ lpszType = "edit"; break;
+ case CGUIControl::GUICONTROL_VISUALISATION:
+ lpszType = "visualisation"; break;
+ case CGUIControl::GUICONTROL_MULTI_IMAGE:
+ lpszType = "multiimage"; break;
+ case CGUIControl::GUICONTROL_GROUP:
+ lpszType = "group"; break;
+ case CGUIControl::GUICONTROL_GROUPLIST:
+ lpszType = "grouplist"; break;
+ case CGUIControl::GUICONTROL_SCROLLBAR:
+ lpszType = "scrollbar"; break;
+ case CGUIControl::GUICONTROL_LISTLABEL:
+ lpszType = "label"; break;
+ case CGUIControl::GUICONTROL_MULTISELECT:
+ lpszType = "multiselect"; break;
+ case CGUIControl::GUICONTAINER_LIST:
+ lpszType = "list"; break;
+ case CGUIControl::GUICONTAINER_WRAPLIST:
+ lpszType = "wraplist"; break;
+ case CGUIControl::GUICONTAINER_FIXEDLIST:
+ lpszType = "fixedlist"; break;
+ case CGUIControl::GUICONTAINER_PANEL:
+ lpszType = "panel"; break;
+ //case CGUIControl::GUICONTROL_LIST:
+ //case CGUIControl::GUICONTROL_UNKNOWN:
+ //case CGUIControl::GUICONTROL_LISTEX:
+ //case CGUIControl::GUICONTROL_SPINBUTTON:
+ //case CGUIControl::GUICONTROL_THUMBNAIL:
+ //case CGUIControl::GUICONTROL_CONSOLE:
+ default:
+ break;
+ }
+
+ if (lpszType)
+ xmlControl->SetAttribute("type", lpszType);
+ if (m_controlID != 0)
+ {
+ CStdString str;
+ str.Format("%u", m_controlID);
+ xmlControl->SetAttribute("id", str.c_str());
+ }
+
+ float pct = (float)GetTotalTime() / (float)m_pProfiler->GetTotalTime();
+ if (pct > 0.01f)
+ {
+ CStdString str;
+ str.Format("%.0f", pct * 100.0f);
+ xmlControl->SetAttribute("percent", str.c_str());
+ }
+
+ if (!m_strDescription.IsEmpty())
+ {
+ TiXmlElement *elem = new TiXmlElement("description");
+ xmlControl->LinkEndChild(elem);
+ TiXmlText *text = new TiXmlText(m_strDescription.c_str());
+ elem->LinkEndChild(text);
+ }
+
+ // Note time is stored in 1/100 milliseconds but reported in ms
+ DWORD dwVis = m_dwVisTime / 100;
+ DWORD dwRend = m_dwRenderTime / 100;
+ if (dwVis || dwRend)
+ {
+ CStdString val;
+ TiXmlElement *elem = new TiXmlElement("rendertime");
+ xmlControl->LinkEndChild(elem);
+ val.Format("%u", dwRend);
+ TiXmlText *text = new TiXmlText(val.c_str());
+ elem->LinkEndChild(text);
+
+ elem = new TiXmlElement("visibletime");
+ xmlControl->LinkEndChild(elem);
+ val.Format("%u", dwVis);
+ text = new TiXmlText(val.c_str());
+ elem->LinkEndChild(text);
+ }
+
+ if (m_vecChildren.size())
+ {
+ TiXmlElement *xmlChilds = new TiXmlElement("children");
+ xmlControl->LinkEndChild(xmlChilds);
+ const unsigned int dwSize = m_vecChildren.size();
+ for (unsigned int i=0; i<dwSize; ++i)
+ m_vecChildren[i]->SaveToXML(xmlChilds);
+ }
+}
+
+CGUIControlProfilerItem *CGUIControlProfilerItem::AddControl(CGUIControl *pControl)
+{
+ m_vecChildren.push_back(new CGUIControlProfilerItem(m_pProfiler, this, pControl));
+ return m_vecChildren.back();
+}
+
+CGUIControlProfilerItem *CGUIControlProfilerItem::FindOrAddControl(CGUIControl *pControl, bool recurse)
+{
+ const unsigned int dwSize = m_vecChildren.size();
+ for (unsigned int i=0; i<dwSize; ++i)
+ {
+ CGUIControlProfilerItem *p = m_vecChildren[i];
+ if (p->m_pControl == pControl)
+ return p;
+ if (recurse && (p = p->FindOrAddControl(pControl, true)))
+ return p;
+ }
+
+ if (pControl->GetParentControl() == m_pControl)
+ return AddControl(pControl);
+
+ return NULL;
+}
+
+CGUIControlProfiler::CGUIControlProfiler(void)
+: m_ItemHead(NULL, NULL, NULL), m_pLastItem(NULL), m_iMaxFrameCount(200)
+// m_bIsRunning(false), no isRunning because it is static
+{
+ LARGE_INTEGER i64PerfFreq;
+ QueryPerformanceFrequency(&i64PerfFreq);
+ m_fPerfScale = 100000.0f / i64PerfFreq.QuadPart;
+}
+
+CGUIControlProfiler &CGUIControlProfiler::Instance(void)
+{
+ static CGUIControlProfiler _instance;
+ return _instance;
+}
+
+bool CGUIControlProfiler::IsRunning(void)
+{
+ return m_bIsRunning;
+}
+
+void CGUIControlProfiler::Start(void)
+{
+ m_iFrameCount = 0;
+ m_bIsRunning = true;
+ m_pLastItem = NULL;
+ m_ItemHead.Reset(this);
+}
+
+void CGUIControlProfiler::BeginVisibility(CGUIControl *pControl)
+{
+ CGUIControlProfilerItem *item = FindOrAddControl(pControl);
+ item->BeginVisibility();
+}
+
+void CGUIControlProfiler::EndVisibility(CGUIControl *pControl)
+{
+ CGUIControlProfilerItem *item = FindOrAddControl(pControl);
+ item->EndVisibility();
+}
+
+void CGUIControlProfiler::BeginRender(CGUIControl *pControl)
+{
+ CGUIControlProfilerItem *item = FindOrAddControl(pControl);
+ item->BeginRender();
+}
+
+void CGUIControlProfiler::EndRender(CGUIControl *pControl)
+{
+ CGUIControlProfilerItem *item = FindOrAddControl(pControl);
+ item->EndRender();
+}
+
+CGUIControlProfilerItem *CGUIControlProfiler::FindOrAddControl(CGUIControl *pControl)
+{
+ if (m_pLastItem)
+ {
+ // Typically calls come in pairs so the last control we found is probably
+ // the one we want again next time
+ if (m_pLastItem->m_pControl == pControl)
+ return m_pLastItem;
+ // If that control is not a match, usually the one we want is the next
+ // sibling of that control, or the parent of that control so check
+ // the parent first as it is more convenient
+ m_pLastItem = m_pLastItem->m_pParent;
+ if (m_pLastItem && m_pLastItem->m_pControl == pControl)
+ return m_pLastItem;
+ // continued from above, this searches the original control's siblings
+ if (m_pLastItem)
+ m_pLastItem = m_pLastItem->FindOrAddControl(pControl, false);
+ if (m_pLastItem)
+ return m_pLastItem;
+ }
+
+ m_pLastItem = m_ItemHead.FindOrAddControl(pControl, true);
+ if (!m_pLastItem)
+ m_pLastItem = m_ItemHead.AddControl(pControl);
+
+ return m_pLastItem;
+}
+
+void CGUIControlProfiler::EndFrame(void)
+{
+ m_iFrameCount++;
+ if (m_iFrameCount >= m_iMaxFrameCount)
+ {
+ const unsigned int dwSize = m_ItemHead.m_vecChildren.size();
+ for (unsigned int i=0; i<dwSize; ++i)
+ {
+ CGUIControlProfilerItem *p = m_ItemHead.m_vecChildren[i];
+ m_ItemHead.m_dwVisTime += p->m_dwVisTime;
+ m_ItemHead.m_dwRenderTime += p->m_dwRenderTime;
+ }
+
+ m_bIsRunning = false;
+ if (SaveResults())
+ m_ItemHead.Reset(this);
+ }
+}
+
+bool CGUIControlProfiler::SaveResults(void)
+{
+ if (m_strOutputFile.IsEmpty())
+ return false;
+
+ TiXmlDocument doc;
+ TiXmlDeclaration decl("1.0", "", "yes");
+ doc.InsertEndChild(decl);
+
+ TiXmlElement *root = new TiXmlElement("guicontrolprofiler");
+ CStdString str;
+ str.Format("%d", m_iFrameCount);
+ root->SetAttribute("framecount", str.c_str());
+ root->SetAttribute("timeunit", "ms");
+ doc.LinkEndChild(root);
+
+ m_ItemHead.SaveToXML(root);
+ return doc.SaveFile(m_strOutputFile);
+}
diff --git a/guilib/GUIControlProfiler.h b/guilib/GUIControlProfiler.h new file mode 100644 index 0000000000..48675e0179 --- /dev/null +++ b/guilib/GUIControlProfiler.h @@ -0,0 +1,102 @@ +/*
+ * Copyright (C) 2005-2009 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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, 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 XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef GUILIB_GUICONTROLPROFILER_H__
+#define GUILIB_GUICONTROLPROFILER_H__
+#pragma once
+
+#include "GUIControl.h"
+
+class CGUIControlProfiler;
+class TiXmlElement;
+
+class CGUIControlProfilerItem
+{
+public:
+ CGUIControlProfiler *m_pProfiler;
+ CGUIControlProfilerItem * m_pParent;
+ CGUIControl *m_pControl;
+ std::vector<CGUIControlProfilerItem *> m_vecChildren;
+ CStdString m_strDescription;
+ int m_controlID;
+ CGUIControl::GUICONTROLTYPES m_ControlType;
+ DWORD m_dwVisTime;
+ DWORD m_dwRenderTime;
+ LARGE_INTEGER m_i64VisStart;
+ LARGE_INTEGER m_i64RenderStart;
+
+ CGUIControlProfilerItem(CGUIControlProfiler *pProfiler, CGUIControlProfilerItem *pParent, CGUIControl *pControl);
+ ~CGUIControlProfilerItem(void);
+
+ void Reset(CGUIControlProfiler *pProfiler);
+ void BeginVisibility(void);
+ void EndVisibility(void);
+ void BeginRender(void);
+ void EndRender(void);
+ void SaveToXML(TiXmlElement *parent);
+ DWORD GetTotalTime(void) const { return m_dwVisTime + m_dwRenderTime; };
+
+ CGUIControlProfilerItem *AddControl(CGUIControl *pControl);
+ CGUIControlProfilerItem *FindOrAddControl(CGUIControl *pControl, bool recurse);
+};
+
+class CGUIControlProfiler
+{
+public:
+ static CGUIControlProfiler &Instance(void);
+ static bool IsRunning(void);
+
+ void Start(void);
+ void EndFrame(void);
+ void BeginVisibility(CGUIControl *pControl);
+ void EndVisibility(CGUIControl *pControl);
+ void BeginRender(CGUIControl *pControl);
+ void EndRender(CGUIControl *pControl);
+ int GetMaxFrameCount(void) const { return m_iMaxFrameCount; };
+ void SetMaxFrameCount(int iMaxFrameCount) { m_iMaxFrameCount = iMaxFrameCount; };
+ void SetOutputFile(const CStdString &strOutputFile) { m_strOutputFile = strOutputFile; };
+ const CStdString &GetOutputFile(void) const { return m_strOutputFile; };
+ bool SaveResults(void);
+ DWORD GetTotalTime(void) const { return m_ItemHead.GetTotalTime(); };
+
+ float m_fPerfScale;
+private:
+ CGUIControlProfiler(void);
+ ~CGUIControlProfiler(void) {};
+ CGUIControlProfiler(const CGUIControlProfiler &that);
+ CGUIControlProfiler &operator=(const CGUIControlProfiler &that);
+
+ CGUIControlProfilerItem m_ItemHead;
+ CGUIControlProfilerItem *m_pLastItem;
+ CGUIControlProfilerItem *FindOrAddControl(CGUIControl *pControl);
+
+ static bool m_bIsRunning;
+ CStdString m_strOutputFile;
+ int m_iMaxFrameCount;
+ int m_iFrameCount;
+};
+
+#define GUIPROFILER_VISIBILITY_BEGIN(x) { if (CGUIControlProfiler::IsRunning()) CGUIControlProfiler::Instance().BeginVisibility(x); }
+#define GUIPROFILER_VISIBILITY_END(x) { if (CGUIControlProfiler::IsRunning()) CGUIControlProfiler::Instance().EndVisibility(x); }
+#define GUIPROFILER_RENDER_BEGIN(x) { if (CGUIControlProfiler::IsRunning()) CGUIControlProfiler::Instance().BeginRender(x); }
+#define GUIPROFILER_RENDER_END(x) { if (CGUIControlProfiler::IsRunning()) CGUIControlProfiler::Instance().EndRender(x); }
+
+#endif
diff --git a/guilib/GUIDialog.cpp b/guilib/GUIDialog.cpp new file mode 100644 index 0000000000..1dd30a7129 --- /dev/null +++ b/guilib/GUIDialog.cpp @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIDialog.h" +#include "GUIWindowManager.h" +#include "GUILabelControl.h" +#include "GUIAudioManager.h" +#include "utils/SingleLock.h" +#include "Application.h" + +CGUIDialog::CGUIDialog(int id, const CStdString &xmlFile) + : CGUIWindow(id, xmlFile) +{ + m_bModal = true; + m_bRunning = false; + m_dialogClosing = false; + m_renderOrder = 1; + m_autoClosing = false; +} + +CGUIDialog::~CGUIDialog(void) +{} + +void CGUIDialog::OnWindowLoaded() +{ + CGUIWindow::OnWindowLoaded(); + + // Clip labels to extents + if (m_children.size()) + { + CGUIControl* pBase = m_children[0]; + + for (iControls p = m_children.begin() + 1; p != m_children.end(); ++p) + { + if ((*p)->GetControlType() == CGUIControl::GUICONTROL_LABEL) + { + CGUILabelControl* pLabel = (CGUILabelControl*)(*p); + + if (!pLabel->GetWidth()) + { + float spacing = (pLabel->GetXPosition() - pBase->GetXPosition()) * 2; + pLabel->SetWidth(pBase->GetWidth() - spacing); + pLabel->SetTruncate(true); + } + } + } + } +} + +bool CGUIDialog::OnAction(const CAction &action) +{ + if (action.id == ACTION_CLOSE_DIALOG || action.id == ACTION_PREVIOUS_MENU) + { + Close(); + return true; + } + return CGUIWindow::OnAction(action); +} + +bool CGUIDialog::OnMessage(CGUIMessage& message) +{ + switch ( message.GetMessage() ) + { + case GUI_MSG_WINDOW_DEINIT: + { + CGUIWindow *pWindow = m_gWindowManager.GetWindow(m_gWindowManager.GetActiveWindow()); + if (pWindow) + m_gWindowManager.ShowOverlay(pWindow->GetOverlayState()); + + CGUIWindow::OnMessage(message); + // if we were running, make sure we remove ourselves from the window manager + if (m_bRunning) + { + m_gWindowManager.RemoveDialog(GetID()); + m_bRunning = false; + m_dialogClosing = false; + m_autoClosing = false; + } + return true; + } + case GUI_MSG_WINDOW_INIT: + { + CGUIWindow::OnMessage(message); + m_showStartTime = timeGetTime(); + return true; + } + } + + return CGUIWindow::OnMessage(message); +} + +void CGUIDialog::Close(bool forceClose /*= false*/) +{ + //Lock graphic context here as it is sometimes called from non rendering threads + //maybe we should have a critical section per window instead?? + CSingleLock lock(g_graphicsContext); + + if (!m_bRunning) return; + + // Play the window specific deinit sound + if(!m_dialogClosing) + g_audioManager.PlayWindowSound(GetID(), SOUND_DEINIT); + + // don't close if we should be animating + if (!forceClose && HasAnimation(ANIM_TYPE_WINDOW_CLOSE)) + { + if (!m_dialogClosing && !IsAnimating(ANIM_TYPE_WINDOW_CLOSE)) + { + QueueAnimation(ANIM_TYPE_WINDOW_CLOSE); + m_dialogClosing = true; + } + return; + } + + CGUIMessage msg(GUI_MSG_WINDOW_DEINIT, 0, 0); + OnMessage(msg); +} + +void CGUIDialog::DoModal_Internal(int iWindowID /*= WINDOW_INVALID */, const CStdString ¶m /* = "" */) +{ + //Lock graphic context here as it is sometimes called from non rendering threads + //maybe we should have a critical section per window instead?? + CSingleLock lock(g_graphicsContext); + + m_dialogClosing = false; + m_bModal = true; + // set running before it's added to the window manager, else the auto-show code + // could show it as well if we are in a different thread from + // the main rendering thread (this should really be handled via + // a thread message though IMO) + m_bRunning = true; + m_gWindowManager.RouteToWindow(this); + + // Play the window specific init sound + g_audioManager.PlayWindowSound(GetID(), SOUND_INIT); + + // active this window... + CGUIMessage msg(GUI_MSG_WINDOW_INIT, 0, 0, WINDOW_INVALID, iWindowID); + msg.SetStringParam(param); + OnMessage(msg); + +// m_bRunning = true; + + if (!m_windowLoaded) + Close(true); + + lock.Leave(); + + while (m_bRunning && !g_application.m_bStop) + { + m_gWindowManager.Process(); + } +} + +void CGUIDialog::Show_Internal() +{ + //Lock graphic context here as it is sometimes called from non rendering threads + //maybe we should have a critical section per window instead?? + CSingleLock lock(g_graphicsContext); + + if (m_bRunning && !m_dialogClosing && !IsAnimating(ANIM_TYPE_WINDOW_CLOSE)) return; + + m_bModal = false; + + // set running before it's added to the window manager, else the auto-show code + // could show it as well if we are in a different thread from + // the main rendering thread (this should really be handled via + // a thread message though IMO) + m_bRunning = true; + m_dialogClosing = false; + m_gWindowManager.AddModeless(this); + + // Play the window specific init sound + g_audioManager.PlayWindowSound(GetID(), SOUND_INIT); + + // active this window... + CGUIMessage msg(GUI_MSG_WINDOW_INIT, 0, 0); + OnMessage(msg); + +// m_bRunning = true; +} + +void CGUIDialog::DoModal(int iWindowID /*= WINDOW_INVALID */, const CStdString ¶m) +{ + g_application.getApplicationMessenger().DoModal(this, iWindowID, param); +} + +void CGUIDialog::Show() +{ + g_application.getApplicationMessenger().Show(this); +} + +bool CGUIDialog::RenderAnimation(DWORD time) +{ + CGUIWindow::RenderAnimation(time); + return m_bRunning; +} + +void CGUIDialog::Render() +{ + CGUIWindow::Render(); + // Check to see if we should close at this point + // We check after the controls have finished rendering, as we may have to close due to + // the controls rendering after the window has finished it's animation + // we call the base class instead of this class so that we can find the change + if (m_dialogClosing && !CGUIWindow::IsAnimating(ANIM_TYPE_WINDOW_CLOSE)) + { + Close(true); + } + + if (m_autoClosing && m_showStartTime + m_showDuration < timeGetTime() && !m_dialogClosing) + { + Close(); + } +} + +bool CGUIDialog::IsAnimating(ANIMATION_TYPE animType) +{ + if (animType == ANIM_TYPE_WINDOW_CLOSE) + return m_dialogClosing; + return CGUIWindow::IsAnimating(animType); +} + +void CGUIDialog::SetDefaults() +{ + CGUIWindow::SetDefaults(); + m_renderOrder = 1; +} + +void CGUIDialog::SetAutoClose(unsigned int timeoutMs) +{ + m_autoClosing = true; + m_showDuration = timeoutMs; + if (m_bRunning) + m_showStartTime = timeGetTime(); +} + + diff --git a/guilib/GUIDialog.h b/guilib/GUIDialog.h new file mode 100644 index 0000000000..e80ace3b6e --- /dev/null +++ b/guilib/GUIDialog.h @@ -0,0 +1,73 @@ +/*! +\file GUIDialog.h +\brief +*/ + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIWindow.h" +#include "Key.h" + +/*! + \ingroup winmsg + \brief + */ +class CGUIDialog : + public CGUIWindow +{ +public: + CGUIDialog(int id, const CStdString &xmlFile); + virtual ~CGUIDialog(void); + + virtual bool OnAction(const CAction &action); + virtual bool OnMessage(CGUIMessage& message); + virtual void Render(); + + void DoModal(int iWindowID = WINDOW_INVALID, const CStdString ¶m = ""); // modal + void Show(); // modeless + + virtual void Close(bool forceClose = false); + virtual bool IsDialogRunning() const { return m_bRunning; }; + virtual bool IsDialog() const { return true;}; + virtual bool IsModalDialog() const { return m_bModal; }; + + virtual bool IsAnimating(ANIMATION_TYPE animType); + + void SetAutoClose(unsigned int timeoutMs); +protected: + virtual bool RenderAnimation(DWORD time); + virtual void SetDefaults(); + virtual void OnWindowLoaded(); + + friend class CApplicationMessenger; + void DoModal_Internal(int iWindowID = WINDOW_INVALID, const CStdString ¶m = ""); // modal + void Show_Internal(); // modeless + + bool m_bRunning; + bool m_bModal; + bool m_dialogClosing; + bool m_autoClosing; + DWORD m_showStartTime; + DWORD m_showDuration; +}; diff --git a/guilib/GUIEditControl.cpp b/guilib/GUIEditControl.cpp new file mode 100644 index 0000000000..604ca384a0 --- /dev/null +++ b/guilib/GUIEditControl.cpp @@ -0,0 +1,482 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIEditControl.h" +#include "utils/CharsetConverter.h" +#include "GUIDialogKeyboard.h" +#include "GUIDialogNumeric.h" +#include "LocalizeStrings.h" +#include "DateTime.h" + +#ifdef __APPLE__ +#include "CocoaInterface.h" +#endif + +using namespace std; + +#ifdef WIN32 +extern HWND g_hWnd; +#endif + +CGUIEditControl::CGUIEditControl(int parentID, int controlID, float posX, float posY, + float width, float height, const CTextureInfo &textureFocus, const CTextureInfo &textureNoFocus, + const CLabelInfo& labelInfo, const std::string &text) + : CGUIButtonControl(parentID, controlID, posX, posY, width, height, textureFocus, textureNoFocus, labelInfo) +{ + ControlType = GUICONTROL_EDIT; + m_textOffset = 0; + m_textWidth = width; + m_cursorPos = 0; + m_cursorBlink = 0; + m_inputHeading = 0; + m_inputType = INPUT_TYPE_TEXT; + SetLabel(text); +} + +CGUIEditControl::CGUIEditControl(const CGUIButtonControl &button) + : CGUIButtonControl(button) +{ + ControlType = GUICONTROL_EDIT; + SetLabel(m_info.GetLabel(GetParentID())); + m_textOffset = 0; + m_textWidth = GetWidth(); + m_cursorPos = 0; + m_cursorBlink = 0; +} + +CGUIEditControl::~CGUIEditControl(void) +{ +} + +bool CGUIEditControl::OnMessage(CGUIMessage &message) +{ + if (message.GetMessage() == GUI_MSG_SET_TYPE) + { + SetInputType((INPUT_TYPE)message.GetParam1(), (int)message.GetParam2()); + return true; + } + else if (message.GetMessage() == GUI_MSG_ITEM_SELECTED) + { + message.SetLabel(GetLabel2()); + return true; + } + return CGUIButtonControl::OnMessage(message); +} + +bool CGUIEditControl::OnAction(const CAction &action) +{ + ValidateCursor(); + + // TODO: We shouldn't really need to test for ACTION_PARENT_DIR here + // but it may currently be useful as we can't map specific to an + // edit control other than the keyboard (which doesn't use this block) + if (action.id == ACTION_BACKSPACE || action.id == ACTION_PARENT_DIR) + { + // backspace + if (m_cursorPos) + { + m_text2.erase(--m_cursorPos, 1); + OnTextChanged(); + } + return true; + } + else if (action.id == ACTION_MOVE_LEFT) + { + if (m_cursorPos > 0) + { + m_cursorPos--; + OnTextChanged(); + return true; + } + } + else if (action.id == ACTION_MOVE_RIGHT) + { + if ((unsigned int) m_cursorPos < m_text2.size()) + { + m_cursorPos++; + OnTextChanged(); + return true; + } + } + else if (action.id == ACTION_PASTE) + { +#ifdef __APPLE__ + const char *szStr = Cocoa_Paste(); + if (szStr) + { + m_text2 += szStr; + m_cursorPos+=strlen(szStr); + OnTextChanged(); + } +#elif defined _WIN32 + HGLOBAL hglb; + LPTSTR lptstr; + if (OpenClipboard(g_hWnd)) + { + hglb = GetClipboardData(CF_TEXT); + if (hglb != NULL) + { + lptstr = (LPTSTR)GlobalLock(hglb); + if (lptstr != NULL) + { + m_text2 = (char*)lptstr; + GlobalUnlock(hglb); + } + } + CloseClipboard(); + OnTextChanged(); + } +#endif + } + else if (action.id >= KEY_VKEY && action.id < KEY_ASCII) + { + // input from the keyboard (vkey, not ascii) + BYTE b = action.id & 0xFF; + if (b == 0x25 && m_cursorPos > 0) + { // left + m_cursorPos--; + OnTextChanged(); + return true; + } + if (b == 0x27 && m_cursorPos < m_text2.length()) + { // right + m_cursorPos++; + OnTextChanged(); + return true; + } + if (b == 0x2e) + { + if (m_cursorPos < m_text2.length()) + { // delete + m_text2.erase(m_cursorPos, 1); + OnTextChanged(); + return true; + } + } + if (b == 0x8) + { + if (m_cursorPos > 0) + { // backspace + m_text2.erase(--m_cursorPos, 1); + OnTextChanged(); + } + return true; + } + } + else if (action.id >= KEY_ASCII) + { + // input from the keyboard + switch (action.unicode) + { + case '\t': + break; + case 10: + case 13: + { + // enter - send click message, but otherwise ignore + SEND_CLICK_MESSAGE(GetID(), GetParentID(), 1); + return true; + } + case 27: + { // escape - fallthrough to default action + return CGUIButtonControl::OnAction(action); + } + case 8: + { + // backspace + if (m_cursorPos) + { + m_text2.erase(--m_cursorPos, 1); + OnTextChanged(); + } + break; + } + default: + { + m_text2.insert(m_text2.begin() + m_cursorPos, (WCHAR)action.unicode); + m_cursorPos++; + OnTextChanged(); + break; + } + } + OnTextChanged(); + return true; + } + else if (action.id >= REMOTE_2 && action.id <= REMOTE_9) + { // input from the remote + if (m_inputType == INPUT_TYPE_FILTER) + { // filtering - use single number presses + m_text2.insert(m_text2.begin() + m_cursorPos, L'0' + (action.id - REMOTE_0)); + m_cursorPos++; + OnTextChanged(); + return true; + } + } + return CGUIButtonControl::OnAction(action); +} + +void CGUIEditControl::OnClick() +{ + // we received a click - it's not from the keyboard, so pop up the virtual keyboard, unless + // that is where we reside! + if (GetParentID() == WINDOW_DIALOG_KEYBOARD) + return; + + CStdString utf8; + g_charsetConverter.wToUTF8(m_text2, utf8); + bool textChanged = false; + CStdString heading = g_localizeStrings.Get(m_inputHeading ? m_inputHeading : 16028); + switch (m_inputType) + { + case INPUT_TYPE_NUMBER: + textChanged = CGUIDialogNumeric::ShowAndGetNumber(utf8, heading); + break; + case INPUT_TYPE_SECONDS: + textChanged = CGUIDialogNumeric::ShowAndGetSeconds(utf8, g_localizeStrings.Get(21420)); + break; + case INPUT_TYPE_DATE: + { + CDateTime dateTime; + dateTime.SetFromDBDate(utf8); + if (dateTime < CDateTime(2000,1, 1, 0, 0, 0)) + dateTime = CDateTime(2000, 1, 1, 0, 0, 0); + SYSTEMTIME date; + dateTime.GetAsSystemTime(date); + if (CGUIDialogNumeric::ShowAndGetDate(date, g_localizeStrings.Get(21420))) + { + dateTime = CDateTime(date); + utf8 = dateTime.GetAsDBDate(); + textChanged = true; + } + break; + } + case INPUT_TYPE_IPADDRESS: + textChanged = CGUIDialogNumeric::ShowAndGetIPAddress(utf8, heading); + break; + case INPUT_TYPE_SEARCH: + CGUIDialogKeyboard::ShowAndGetFilter(utf8, true); + break; + case INPUT_TYPE_FILTER: + CGUIDialogKeyboard::ShowAndGetFilter(utf8, false); + break; + case INPUT_TYPE_TEXT: + default: + textChanged = CGUIDialogKeyboard::ShowAndGetInput(utf8, heading, true, m_inputType == INPUT_TYPE_PASSWORD); + break; + } + if (textChanged) + { + g_charsetConverter.utf8ToW(utf8, m_text2); + m_cursorPos = m_text2.size(); + OnTextChanged(); + m_cursorPos = m_text2.size(); + } +} + +void CGUIEditControl::SetInputType(CGUIEditControl::INPUT_TYPE type, int heading) +{ + m_inputType = type; + m_inputHeading = heading; + // TODO: Verify the current input string? +} + +void CGUIEditControl::RecalcLabelPosition() +{ + if (!m_label.font) return; + + // ensure that our cursor is within our width + ValidateCursor(); + + CStdStringW text = GetDisplayedText(); + m_textWidth = m_textLayout2.GetTextWidth(text + L'|'); + float beforeCursorWidth = m_textLayout2.GetTextWidth(text.Left(m_cursorPos)); + float afterCursorWidth = m_textLayout2.GetTextWidth(text.Left(m_cursorPos) + L'|'); + float leftTextWidth = m_textLayout.GetTextWidth(); + float maxTextWidth = m_width - m_label.offsetX*2; + if (leftTextWidth > 0) + maxTextWidth -= leftTextWidth + spaceWidth; + + // if skinner forgot to set height :p + if (m_height == 0) + m_height = 2*m_label.font->GetTextHeight(1); + + if (m_textWidth > maxTextWidth) + { // we render taking up the full width, so make sure our cursor position is + // within the render window + if (m_textOffset + afterCursorWidth > maxTextWidth) + { + // move the position to the left (outside of the viewport) + m_textOffset = maxTextWidth - afterCursorWidth; + } + else if (m_textOffset + beforeCursorWidth < 0) // offscreen to the left + { + // otherwise use original position + m_textOffset = -beforeCursorWidth; + } + else if (m_textOffset + m_textWidth < maxTextWidth) + { // we have more text than we're allowed, but we aren't filling all the space + m_textOffset = maxTextWidth - m_textWidth; + } + } + else + m_textOffset = 0; +} + +void CGUIEditControl::RenderText() +{ + if (m_bInvalidated) + RecalcLabelPosition(); + + float leftTextWidth = m_textLayout.GetTextWidth(); + float maxTextWidth = m_width - m_label.offsetX * 2; + + // start by rendering the normal text + float posX = m_posX + m_label.offsetX; + float posY = m_posY; + uint32_t align = m_label.align & XBFONT_CENTER_Y; + + if (m_label.align & XBFONT_CENTER_Y) + posY += m_height*0.5f; + + if (leftTextWidth > 0) + { + // render the text on the left + if (IsDisabled()) + m_textLayout.Render(posX, posY, m_label.angle, m_label.disabledColor, m_label.shadowColor, align, leftTextWidth, true); + else if (HasFocus() && m_label.focusedColor) + m_textLayout.Render(posX, posY, m_label.angle, m_label.focusedColor, m_label.shadowColor, align, leftTextWidth); + else + m_textLayout.Render(posX, posY, m_label.angle, m_label.textColor, m_label.shadowColor, align, leftTextWidth); + + posX += leftTextWidth + spaceWidth; + maxTextWidth -= leftTextWidth + spaceWidth; + } + + if (g_graphicsContext.SetClipRegion(posX, m_posY, maxTextWidth, m_height)) + { + if (m_textWidth < maxTextWidth) + { // align text as our text fits + if (leftTextWidth > 0) + { // right align as we have 2 labels + posX = m_posX + m_width - m_label.offsetX; + align |= XBFONT_RIGHT; + } + else + { // align by whatever the skinner requests + if (m_label.align & XBFONT_CENTER_X) + posX += 0.5f*maxTextWidth; + if (m_label.align & XBFONT_RIGHT) + posX += maxTextWidth; + align |= (m_label.align & 3); + } + } + CStdStringW text = GetDisplayedText(); + // let's render it ourselves + if (HasFocus()) + { // cursor location assumes utf16 text, so deal with that (inefficient, but it's not as if it's a high-use area + // virtual keyboard only) + CStdStringW col; + if ((m_dwFocusCounter % 64) > 32) + col.Format(L"|"); + else + col.Format(L"[COLOR %x]|[/COLOR]", 0x1000000); + text.Insert(m_cursorPos, col); + } + + m_textLayout2.SetText(text); + + if (IsDisabled()) + m_textLayout2.Render(posX + m_textOffset, posY, m_label.angle, m_label.disabledColor, m_label.shadowColor, align, m_textWidth, true); + else if (HasFocus() && m_label.focusedColor) + m_textLayout2.Render(posX + m_textOffset, posY, m_label.angle, m_label.focusedColor, m_label.shadowColor, align, m_textWidth); + else + m_textLayout2.Render(posX + m_textOffset, posY, m_label.angle, m_label.textColor, m_label.shadowColor, align, m_textWidth); + + g_graphicsContext.RestoreClipRegion(); + } +} + +CStdStringW CGUIEditControl::GetDisplayedText() const +{ + if (m_inputType == INPUT_TYPE_PASSWORD) + { + CStdStringW text; + text.append(m_text2.size(), L'*'); + return text; + } + return m_text2; +} + +void CGUIEditControl::ValidateCursor() +{ + if (m_cursorPos > m_text2.size()) + m_cursorPos = m_text2.size(); +} + +void CGUIEditControl::OnTextChanged() +{ + SEND_CLICK_MESSAGE(GetID(), GetParentID(), 0); + + vector<CGUIActionDescriptor> textChangeActions = m_textChangeActions; + for (unsigned int i = 0; i < textChangeActions.size(); i++) + { + CGUIMessage message(GUI_MSG_EXECUTE, GetID(), GetParentID()); + message.SetAction(textChangeActions[i]); + g_graphicsContext.SendMessage(message); + } + + SetInvalid(); +} + +void CGUIEditControl::SetLabel(const std::string &text) +{ + m_textLayout.Update(text); + SetInvalid(); +} + +void CGUIEditControl::SetLabel2(const std::string &text) +{ + CStdStringW newText; + g_charsetConverter.utf8ToW(text, newText); + if (newText != m_text2) + { + m_text2 = newText; + m_cursorPos = m_text2.size(); + SetInvalid(); + } +} + +CStdString CGUIEditControl::GetLabel2() const +{ + CStdString text; + g_charsetConverter.wToUTF8(m_text2, text); + return text; +} + +unsigned int CGUIEditControl::GetCursorPosition() const +{ + return m_cursorPos; +} + +void CGUIEditControl::SetCursorPosition(unsigned int iPosition) +{ + m_cursorPos = iPosition; +} diff --git a/guilib/GUIEditControl.h b/guilib/GUIEditControl.h new file mode 100644 index 0000000000..8f8b75ad12 --- /dev/null +++ b/guilib/GUIEditControl.h @@ -0,0 +1,100 @@ +/*! +\file GUIEditControl.h +\brief +*/ + +#ifndef GUILIB_GUIEditControl_H +#define GUILIB_GUIEditControl_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIButtonControl.h" + +/*! + \ingroup controls + \brief + */ + +class CGUIEditControl : public CGUIButtonControl +{ +public: + enum INPUT_TYPE { + INPUT_TYPE_TEXT = 0, + INPUT_TYPE_NUMBER, + INPUT_TYPE_SECONDS, + INPUT_TYPE_DATE, + INPUT_TYPE_IPADDRESS, + INPUT_TYPE_PASSWORD, + INPUT_TYPE_SEARCH, + INPUT_TYPE_FILTER + }; + + CGUIEditControl(int parentID, int controlID, float posX, float posY, + float width, float height, const CTextureInfo &textureFocus, const CTextureInfo &textureNoFocus, + const CLabelInfo& labelInfo, const std::string &text); + CGUIEditControl(const CGUIButtonControl &button); + virtual ~CGUIEditControl(void); + virtual CGUIEditControl *Clone() const { return new CGUIEditControl(*this); }; + + virtual bool OnMessage(CGUIMessage &message); + virtual bool OnAction(const CAction &action); + virtual void OnClick(); + + virtual void SetLabel(const std::string &text); + virtual void SetLabel2(const std::string &text); + + virtual CStdString GetLabel2() const; + + unsigned int GetCursorPosition() const; + void SetCursorPosition(unsigned int iPosition); + + void SetInputType(INPUT_TYPE type, int heading); + + void SetTextChangeActions(const std::vector<CGUIActionDescriptor>& textChangeActions) { m_textChangeActions = textChangeActions; }; + + bool HasTextChangeActions() { return m_textChangeActions.size() > 0; }; + +protected: + virtual void RenderText(); + CStdStringW GetDisplayedText() const; + void RecalcLabelPosition(); + void ValidateCursor(); + void OnTextChanged(); + + CStdStringW m_text2; + CStdString m_text; + float m_textOffset; + float m_textWidth; + + static const int spaceWidth = 5; + + unsigned int m_cursorPos; + unsigned int m_cursorBlink; + + int m_inputHeading; + INPUT_TYPE m_inputType; + + std::vector<CGUIActionDescriptor> m_textChangeActions; +}; +#endif diff --git a/guilib/GUIFadeLabelControl.cpp b/guilib/GUIFadeLabelControl.cpp new file mode 100644 index 0000000000..195740434e --- /dev/null +++ b/guilib/GUIFadeLabelControl.cpp @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIFadeLabelControl.h" +#include "utils/CharsetConverter.h" + +using namespace std; + +CGUIFadeLabelControl::CGUIFadeLabelControl(int parentID, int controlID, float posX, float posY, float width, float height, const CLabelInfo& labelInfo, bool scrollOut, int scrollSpeed, DWORD timeToDelayAtEnd, bool resetOnLabelChange) + : CGUIControl(parentID, controlID, posX, posY, width, height), m_scrollInfo(50, labelInfo.offsetX, scrollSpeed) + , m_textLayout(labelInfo.font, false) +{ + m_label = labelInfo; + m_currentLabel = 0; + ControlType = GUICONTROL_FADELABEL; + m_scrollOut = scrollOut; + m_fadeAnim = CAnimation::CreateFader(100, 0, timeToDelayAtEnd, 200); + if (m_fadeAnim) + m_fadeAnim->ApplyAnimation(); + m_renderTime = 0; + m_lastLabel = -1; + m_scrollSpeed = scrollSpeed; // save it for later + m_resetOnLabelChange = resetOnLabelChange; + m_shortText = false; +} + +CGUIFadeLabelControl::CGUIFadeLabelControl(const CGUIFadeLabelControl &from) +: CGUIControl(from), m_infoLabels(from.m_infoLabels), m_scrollInfo(from.m_scrollInfo), m_textLayout(from.m_textLayout) +{ + m_label = from.m_label; + m_scrollOut = from.m_scrollOut; + m_scrollSpeed = from.m_scrollSpeed; + m_resetOnLabelChange = from.m_resetOnLabelChange; + + if (from.m_fadeAnim) + m_fadeAnim = new CAnimation(*from.m_fadeAnim); + if (m_fadeAnim) + m_fadeAnim->ApplyAnimation(); + m_currentLabel = 0; + m_renderTime = 0; + m_lastLabel = -1; + ControlType = GUICONTROL_FADELABEL; +} + +CGUIFadeLabelControl::~CGUIFadeLabelControl(void) +{ + delete m_fadeAnim; +} + +void CGUIFadeLabelControl::SetInfo(const vector<CGUIInfoLabel> &infoLabels) +{ + m_lastLabel = -1; + m_infoLabels = infoLabels; +} + +void CGUIFadeLabelControl::AddLabel(const string &label) +{ + m_infoLabels.push_back(CGUIInfoLabel(label)); +} + +void CGUIFadeLabelControl::DoRender(DWORD currentTime) +{ + m_renderTime = currentTime; + CGUIControl::DoRender(currentTime); +} + +void CGUIFadeLabelControl::UpdateColors() +{ + m_label.UpdateColors(); + CGUIControl::UpdateColors(); +} + +void CGUIFadeLabelControl::Render() +{ + if (m_infoLabels.size() == 0) + { // nothing to render + CGUIControl::Render(); + return ; + } + + if (m_currentLabel >= m_infoLabels.size() ) + m_currentLabel = 0; + + if (m_textLayout.Update(m_infoLabels[m_currentLabel].GetLabel(m_parentID))) + { // changed label - update our suffix based on length of available text + float width, height; + m_textLayout.GetTextExtent(width, height); + float spaceWidth = m_label.font->GetCharWidth(L' '); + unsigned int numSpaces = (unsigned int)(m_width / spaceWidth) + 1; + if (width < m_width) // append spaces for scrolling + numSpaces += (unsigned int)((m_width - width) / spaceWidth) + 1; + m_shortText = (width + m_label.offsetX) < m_width; + m_scrollInfo.suffix.assign(numSpaces, L' '); + if (m_resetOnLabelChange) + { + m_scrollInfo.Reset(); + m_fadeAnim->ResetAnimation(); + } + } + if (m_currentLabel != m_lastLabel) + { // new label - reset scrolling + m_scrollInfo.Reset(); + m_fadeAnim->QueueAnimation(ANIM_PROCESS_REVERSE); + m_lastLabel = m_currentLabel; + } + + float posY = m_posY; + if (m_label.align & XBFONT_CENTER_Y) + posY += m_height * 0.5f; + if (m_infoLabels.size() == 1 && m_shortText) + { // single label set and no scrolling required - just display + m_textLayout.Render(m_posX + m_label.offsetX, posY, 0, m_label.textColor, m_label.shadowColor, (m_label.align & ~3), m_width - m_label.offsetX); + CGUIControl::Render(); + return; + } + + bool moveToNextLabel = false; + if (!m_scrollOut) + { + vecText text; + m_textLayout.GetFirstText(text); + if (m_scrollInfo.characterPos && m_scrollInfo.characterPos < text.size()) + text.erase(text.begin(), text.begin() + min((int)m_scrollInfo.characterPos - 1, (int)text.size())); + if (m_label.font->GetTextWidth(text) < m_width) + { + if (m_fadeAnim->GetProcess() != ANIM_PROCESS_NORMAL) + m_fadeAnim->QueueAnimation(ANIM_PROCESS_NORMAL); + moveToNextLabel = true; + } + } + else if (m_scrollInfo.characterPos > m_textLayout.GetTextLength()) + moveToNextLabel = true; + + // apply the fading animation + TransformMatrix matrix; + m_fadeAnim->Animate(m_renderTime, true); + m_fadeAnim->RenderAnimation(matrix); + g_graphicsContext.AddTransform(matrix); + + if (m_fadeAnim->GetState() == ANIM_STATE_APPLIED) + m_fadeAnim->ResetAnimation(); + + m_scrollInfo.SetSpeed((m_fadeAnim->GetProcess() == ANIM_PROCESS_NONE) ? m_scrollSpeed : 0); + + if (!m_scrollOut && m_shortText) + { + float posX = m_posX + m_label.offsetX; + if (m_label.align & XBFONT_CENTER_X) + posX = m_posX + m_width * 0.5f; + else if (m_label.align & XBFONT_RIGHT) + posX = m_posX + m_width; + m_textLayout.Render(posX, posY, 0, m_label.textColor, m_label.shadowColor, m_label.align, m_width); + } + else + m_textLayout.RenderScrolling(m_posX, posY, 0, m_label.textColor, m_label.shadowColor, (m_label.align & ~3), m_width, m_scrollInfo); + + if (moveToNextLabel) + { // increment the label and reset scrolling + if (m_fadeAnim->GetProcess() != ANIM_PROCESS_NORMAL) + { + unsigned int label = m_currentLabel; + do + { // cycle until we get a non-empty label, or stick with the one we have + label++; + if (label >= m_infoLabels.size()) + label = 0; + } + while (label != m_currentLabel && m_infoLabels[label].GetLabel(m_parentID).IsEmpty()); + m_currentLabel = label; + m_scrollInfo.Reset(); + m_fadeAnim->QueueAnimation(ANIM_PROCESS_REVERSE); + } + } + + g_graphicsContext.RemoveTransform(); + + CGUIControl::Render(); +} + + +bool CGUIFadeLabelControl::CanFocus() const +{ + return false; +} + + +bool CGUIFadeLabelControl::OnMessage(CGUIMessage& message) +{ + if ( message.GetControlId() == GetID() ) + { + if (message.GetMessage() == GUI_MSG_LABEL_ADD) + { + AddLabel(message.GetLabel()); + } + if (message.GetMessage() == GUI_MSG_LABEL_RESET) + { + m_lastLabel = -1; + m_infoLabels.clear(); + m_scrollInfo.Reset(); + } + if (message.GetMessage() == GUI_MSG_LABEL_SET) + { + m_lastLabel = -1; + m_infoLabels.clear(); + m_scrollInfo.Reset(); + AddLabel(message.GetLabel()); + } + } + return CGUIControl::OnMessage(message); +} + diff --git a/guilib/GUIFadeLabelControl.h b/guilib/GUIFadeLabelControl.h new file mode 100644 index 0000000000..bfa73d9557 --- /dev/null +++ b/guilib/GUIFadeLabelControl.h @@ -0,0 +1,75 @@ +/*! +\file GUIFadeLabelControl.h +\brief +*/ + +#ifndef GUILIB_GUIFADELABELCONTROL_H +#define GUILIB_GUIFADELABELCONTROL_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControl.h" + +#include "GUILabelControl.h" // for CInfoPortion + +/*! + \ingroup controls + \brief + */ +class CGUIFadeLabelControl : public CGUIControl +{ +public: + CGUIFadeLabelControl(int parentID, int controlID, float posX, float posY, float width, float height, const CLabelInfo& labelInfo, bool scrollOut, int scrollSpeed, DWORD timeToDelayAtEnd, bool resetOnLabelChange); + CGUIFadeLabelControl(const CGUIFadeLabelControl &from); + virtual ~CGUIFadeLabelControl(void); + virtual CGUIFadeLabelControl *Clone() const { return new CGUIFadeLabelControl(*this); }; + + virtual void DoRender(DWORD currentTime); + virtual void Render(); + virtual bool CanFocus() const; + virtual bool OnMessage(CGUIMessage& message); + + void SetInfo(const std::vector<CGUIInfoLabel> &vecInfo); + +protected: + virtual void UpdateColors(); + void AddLabel(const std::string &label); + + std::vector< CGUIInfoLabel > m_infoLabels; + unsigned int m_currentLabel; + unsigned int m_lastLabel; + + CLabelInfo m_label; + + bool m_scrollOut; // true if we scroll the text all the way to the left before fading in the next label + bool m_shortText; // true if the text we have is shorter than the width of the control + + CScrollInfo m_scrollInfo; + CGUITextLayout m_textLayout; + CAnimation *m_fadeAnim; + DWORD m_renderTime; + unsigned int m_scrollSpeed; + bool m_resetOnLabelChange; +}; +#endif diff --git a/guilib/GUIFixedListContainer.cpp b/guilib/GUIFixedListContainer.cpp new file mode 100644 index 0000000000..aa14f94c2d --- /dev/null +++ b/guilib/GUIFixedListContainer.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIFixedListContainer.h" +#include "GUIListItem.h" +#include "utils/GUIInfoManager.h" +#include "Key.h" + +CGUIFixedListContainer::CGUIFixedListContainer(int parentID, int controlID, float posX, float posY, float width, float height, ORIENTATION orientation, int scrollTime, int preloadItems, int fixedPosition) + : CGUIBaseContainer(parentID, controlID, posX, posY, width, height, orientation, scrollTime, preloadItems) +{ + ControlType = GUICONTAINER_FIXEDLIST; + m_type = VIEW_TYPE_LIST; + m_cursor = fixedPosition; +} + +CGUIFixedListContainer::~CGUIFixedListContainer(void) +{ +} + +bool CGUIFixedListContainer::OnAction(const CAction &action) +{ + switch (action.id) + { + case ACTION_PAGE_UP: + { + if (m_offset + m_cursor < m_itemsPerPage) + { // already on the first page, so move to the first item + Scroll(m_offset + m_cursor - m_itemsPerPage); + } + else + { // scroll up to the previous page + Scroll(-m_itemsPerPage); + } + return true; + } + break; + case ACTION_PAGE_DOWN: + { + if (m_offset + m_cursor >= (int)m_items.size() || (int)m_items.size() < m_itemsPerPage) + { // already at the last page, so move to the last item. + Scroll(m_items.size() - 1 - m_offset + m_cursor); + } + else + { // scroll down to the next page + Scroll(m_itemsPerPage); + } + return true; + } + break; + // smooth scrolling (for analog controls) + case ACTION_SCROLL_UP: + { + m_analogScrollCount += action.amount1 * action.amount1; + bool handled = false; + while (m_analogScrollCount > 0.4) + { + handled = true; + m_analogScrollCount -= 0.4f; + if (m_offset > -m_cursor) + Scroll(-1); + } + return handled; + } + break; + case ACTION_SCROLL_DOWN: + { + m_analogScrollCount += action.amount1 * action.amount1; + bool handled = false; + while (m_analogScrollCount > 0.4) + { + handled = true; + m_analogScrollCount -= 0.4f; + if (m_offset < (int)m_items.size() - 1) + Scroll(1); + } + return handled; + } + break; + } + return CGUIBaseContainer::OnAction(action); +} + +bool CGUIFixedListContainer::OnMessage(CGUIMessage& message) +{ + if (message.GetControlId() == GetID() ) + { + if (message.GetMessage() == GUI_MSG_ITEM_SELECT) + { + SelectItem(message.GetParam1()); + return true; + } + } + return CGUIBaseContainer::OnMessage(message); +} + +bool CGUIFixedListContainer::MoveUp(bool wrapAround) +{ + if (m_offset > -m_cursor) + ScrollToOffset(m_offset - 1); + else if (wrapAround) + { + if (m_items.size() > 0) + { // move 2 last item in list + int offset = m_items.size() - m_cursor - 1; + if (offset < -m_cursor) offset = -m_cursor; + ScrollToOffset(offset); + g_infoManager.SetContainerMoving(GetID(), -1); + } + } + else + return false; + return true; +} + +bool CGUIFixedListContainer::MoveDown(bool wrapAround) +{ + if (m_offset + m_cursor + 1 < (int)m_items.size()) + { + ScrollToOffset(m_offset + 1); + } + else if (wrapAround) + { // move first item in list + ScrollToOffset(-m_cursor); + g_infoManager.SetContainerMoving(GetID(), 1); + } + else + return false; + return true; +} + +// scrolls the said amount +void CGUIFixedListContainer::Scroll(int amount) +{ + // increase or decrease the offset + int offset = m_offset + amount; + if (offset >= (int)m_items.size() - m_cursor) + { + offset = m_items.size() - m_cursor - 1; + } + if (offset < -m_cursor) offset = -m_cursor; + ScrollToOffset(offset); +} + +void CGUIFixedListContainer::ValidateOffset() +{ // first thing is we check the range of m_offset + if (!m_layout) return; + if (m_offset > (int)m_items.size() - m_cursor) + { + m_offset = m_items.size() - m_cursor; + m_scrollOffset = m_offset * m_layout->Size(m_orientation); + } + if (m_offset < -m_cursor) + { + m_offset = -m_cursor; + m_scrollOffset = m_offset * m_layout->Size(m_orientation); + } +} + +bool CGUIFixedListContainer::SelectItemFromPoint(const CPoint &point) +{ + if (!m_focusedLayout || !m_layout) + return false; + + const float mouse_scroll_speed = 0.05f; + const float mouse_max_amount = 1.5f; // max speed: 1 item per frame + float sizeOfItem = m_layout->Size(m_orientation); + // see if the point is either side of our focused item + float start = m_cursor * sizeOfItem; + float end = start + m_focusedLayout->Size(m_orientation); + float pos = (m_orientation == VERTICAL) ? point.y : point.x; + if (pos < start - 0.5f * sizeOfItem) + { // scroll backward + if (!InsideLayout(m_layout, point)) + return false; + float amount = std::min((start - pos) / sizeOfItem, mouse_max_amount); + m_analogScrollCount += amount * amount * mouse_scroll_speed; + if (m_analogScrollCount > 1) + { + Scroll(-1); + m_analogScrollCount-=1.0f; + } + return true; + } + else if (pos > end + 0.5f * sizeOfItem) + { + if (!InsideLayout(m_layout, point)) + return false; + // scroll forward + float amount = std::min((pos - end) / sizeOfItem, mouse_max_amount); + m_analogScrollCount += amount * amount * mouse_scroll_speed; + if (m_analogScrollCount > 1) + { + Scroll(1); + m_analogScrollCount-=1.0f; + } + return true; + } + return InsideLayout(m_focusedLayout, point); +} + +void CGUIFixedListContainer::SelectItem(int item) +{ + // Check that m_offset is valid + ValidateOffset(); + // only select an item if it's in a valid range + if (item >= 0 && item < (int)m_items.size()) + { + // Select the item requested + ScrollToOffset(item - m_cursor); + } +} + +bool CGUIFixedListContainer::HasPreviousPage() const +{ + return (m_offset > 0); +} + +bool CGUIFixedListContainer::HasNextPage() const +{ + return (m_offset != (int)m_items.size() - m_itemsPerPage && (int)m_items.size() >= m_itemsPerPage); +} + +int CGUIFixedListContainer::GetCurrentPage() const +{ + int offset = CorrectOffset(m_offset, m_cursor); + if (offset + m_itemsPerPage - m_cursor >= (int)GetRows()) // last page + return (GetRows() + m_itemsPerPage - 1) / m_itemsPerPage; + return offset / m_itemsPerPage + 1; +} + diff --git a/guilib/GUIFixedListContainer.h b/guilib/GUIFixedListContainer.h new file mode 100644 index 0000000000..f79836bd56 --- /dev/null +++ b/guilib/GUIFixedListContainer.h @@ -0,0 +1,56 @@ +/*! +\file GUIFixedListContainer.h +\brief +*/ + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIBaseContainer.h" + +/*! + \ingroup controls + \brief + */ +class CGUIFixedListContainer : public CGUIBaseContainer +{ +public: + CGUIFixedListContainer(int parentID, int controlID, float posX, float posY, float width, float height, ORIENTATION orientation, int scrollTime, int preloadItems, int fixedPosition); + virtual ~CGUIFixedListContainer(void); + virtual CGUIFixedListContainer *Clone() const { return new CGUIFixedListContainer(*this); }; + + virtual bool OnAction(const CAction &action); + virtual bool OnMessage(CGUIMessage& message); + +protected: + virtual void Scroll(int amount); + virtual bool MoveDown(bool wrapAround); + virtual bool MoveUp(bool wrapAround); + virtual void ValidateOffset(); + virtual bool SelectItemFromPoint(const CPoint &point); + virtual void SelectItem(int item); + virtual bool HasNextPage() const; + virtual bool HasPreviousPage() const; + virtual int GetCurrentPage() const; +}; + diff --git a/guilib/GUIFont.cpp b/guilib/GUIFont.cpp new file mode 100644 index 0000000000..d80e3ab39d --- /dev/null +++ b/guilib/GUIFont.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIFont.h" +#include "GUIFontTTF.h" +#include "GraphicContext.h" + +#include "utils/SingleLock.h" +#include "MathUtils.h" + +#define ROUND(x) (float)(MathUtils::round_int(x)) + +float CScrollInfo::GetPixelsPerFrame() +{ + static const float alphaEMA = 0.05f; + + if (0 == pixelSpeed) + return 0; // not scrolling + DWORD currentTime = timeGetTime(); + float delta = m_lastFrameTime ? (float)(currentTime - m_lastFrameTime) : m_averageFrameTime; + if (delta > 100) + delta = 100; // assume a minimum of 10 fps + m_lastFrameTime = currentTime; + // do an exponential moving average of the frame time + m_averageFrameTime = m_averageFrameTime + (delta - m_averageFrameTime) * alphaEMA; + // and multiply by pixel speed (per ms) to get number of pixels to move this frame + return pixelSpeed * m_averageFrameTime; +} + +CGUIFont::CGUIFont(const CStdString& strFontName, uint32_t style, color_t textColor, color_t shadowColor, float lineSpacing, CGUIFontTTFBase *font) +{ + m_strFontName = strFontName; + m_style = style & 3; + m_textColor = textColor; + m_shadowColor = shadowColor; + m_lineSpacing = lineSpacing; + m_font = font; + + if (m_font) + m_font->AddReference(); +} + +CGUIFont::~CGUIFont() +{ + if (m_font) + m_font->RemoveReference(); +} + +CStdString& CGUIFont::GetFontName() +{ + return m_strFontName; +} + +void CGUIFont::DrawText( float x, float y, const vecColors &colors, color_t shadowColor, + const vecText &text, uint32_t alignment, float maxPixelWidth) +{ + if (!m_font) return; + + bool clip = maxPixelWidth > 0; + if (clip && ClippedRegionIsEmpty(x, y, maxPixelWidth, alignment)) + return; + + maxPixelWidth = ROUND(maxPixelWidth / g_graphicsContext.GetGUIScaleX()); + vecColors renderColors; + for (unsigned int i = 0; i < colors.size(); i++) + renderColors.push_back(g_graphicsContext.MergeAlpha(colors[i] ? colors[i] : m_textColor)); + if (!shadowColor) shadowColor = m_shadowColor; + if (shadowColor) + m_font->DrawTextInternal(x + 1, y + 1, g_graphicsContext.MergeAlpha(shadowColor), text, alignment, maxPixelWidth, false); + m_font->DrawTextInternal( x, y, renderColors, text, alignment, maxPixelWidth, false); + + if (clip) + g_graphicsContext.RestoreClipRegion(); +} + +void CGUIFont::DrawScrollingText(float x, float y, const vecColors &colors, color_t shadowColor, + const vecText &text, uint32_t alignment, float maxWidth, CScrollInfo &scrollInfo) +{ + if (!m_font) return; + if (!shadowColor) shadowColor = m_shadowColor; + + float spaceWidth = GetCharWidth(L' '); + // max chars on screen + extra margin chars + vecText::size_type maxChars = + std::min<vecText::size_type>( + (text.size() + (vecText::size_type)scrollInfo.suffix.size()), + (vecText::size_type)((maxWidth * 1.05f) / spaceWidth)); + + if (!text.size() || ClippedRegionIsEmpty(x, y, maxWidth, alignment)) + return; // nothing to render + + maxWidth = ROUND(maxWidth / g_graphicsContext.GetGUIScaleX()); + + // draw at our scroll position + // we handle the scrolling as follows: + // We scroll on a per-pixel basis up until we have scrolled the first character outside + // of our viewport, whereby we cycle the string around, and reset the scroll position. + // + // pixelPos is the amount in pixels to move the string by. + // characterPos is the amount in characters to rotate the string by. + // + float offset = scrollInfo.pixelPos; + if (!scrollInfo.waitTime) + { + // move along by the appropriate scroll amount + float scrollAmount = fabs(scrollInfo.GetPixelsPerFrame() * g_graphicsContext.GetGUIScaleX()); + + if (scrollInfo.pixelSpeed > 0) + { + // we want to move scrollAmount, grab the next character + float charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text)); + if (scrollInfo.pixelPos + scrollAmount < charWidth) + scrollInfo.pixelPos += scrollAmount; // within the current character + else + { // past the current character, decrement scrollAmount by the charWidth and move to the next character + while (scrollInfo.pixelPos + scrollAmount >= charWidth) + { + scrollAmount -= (charWidth - scrollInfo.pixelPos); + scrollInfo.pixelPos = 0; + scrollInfo.characterPos++; + if (scrollInfo.characterPos >= text.size() + scrollInfo.suffix.size()) + { + scrollInfo.Reset(); + break; + } + charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text)); + } + } + offset = scrollInfo.pixelPos; + } + else if (scrollInfo.pixelSpeed < 0) + { // scrolling backwards + // we want to move scrollAmount, grab the next character + float charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text)); + if (scrollInfo.pixelPos + scrollAmount < charWidth) + scrollInfo.pixelPos += scrollAmount; // within the current character + else + { // past the current character, decrement scrollAmount by the charWidth and move to the next character + while (scrollInfo.pixelPos + scrollAmount >= charWidth) + { + scrollAmount -= (charWidth - scrollInfo.pixelPos); + scrollInfo.pixelPos = 0; + if (scrollInfo.characterPos == 0) + { + scrollInfo.Reset(); + scrollInfo.characterPos = text.size() + scrollInfo.suffix.size() - 1; + break; + } + scrollInfo.characterPos--; + charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text)); + } + } + offset = charWidth - scrollInfo.pixelPos; + } + } + else + scrollInfo.waitTime--; + + // Now rotate our string as needed, only take a slightly larger then visible part of the text. + unsigned int pos = scrollInfo.characterPos; + vecText renderText; + renderText.reserve(maxChars); + for (vecText::size_type i = 0; i < maxChars; i++) + { + if (pos >= text.size() + scrollInfo.suffix.size()) + pos = 0; + if (pos < text.size()) + renderText.push_back(text[pos]); + else + renderText.push_back(scrollInfo.suffix[pos - text.size()]); + pos++; + } + + vecColors renderColors; + for (unsigned int i = 0; i < colors.size(); i++) + renderColors.push_back(g_graphicsContext.MergeAlpha(colors[i] ? colors[i] : m_textColor)); + + bool scroll = !scrollInfo.waitTime && scrollInfo.pixelSpeed; + if (shadowColor) + m_font->DrawTextInternal(x - offset + 1, y + 1, g_graphicsContext.MergeAlpha(shadowColor), renderText, alignment, maxWidth + scrollInfo.pixelPos + m_font->GetLineHeight(2.0f), scroll); + + m_font->DrawTextInternal(x - offset, y, renderColors, renderText, alignment, maxWidth + scrollInfo.pixelPos + m_font->GetLineHeight(2.0f), scroll); + + g_graphicsContext.RestoreClipRegion(); +} + +// remaps unsupported font glpyhs to other suitable ones +wchar_t CGUIFont::RemapGlyph(wchar_t letter) +{ + if (letter == 0x2019 || letter == 0x2018) return 0x0027; // single quotes + else if (letter == 0x201c || letter == 0x201d) return 0x0022; + return 0; // no decent character map +} + +bool CGUIFont::ClippedRegionIsEmpty(float x, float y, float width, uint32_t alignment) const +{ + if (alignment & XBFONT_CENTER_X) + x -= width * 0.5f; + else if (alignment & XBFONT_RIGHT) + x -= width; + if (alignment & XBFONT_CENTER_Y) + y -= m_font->GetLineHeight(m_lineSpacing); + + return !g_graphicsContext.SetClipRegion(x, y, width, m_font->GetLineHeight(2.0f) * g_graphicsContext.GetGUIScaleY()); +} + +float CGUIFont::GetTextWidth( const vecText &text ) +{ + if (!m_font) return 0; + CSingleLock lock(g_graphicsContext); + return m_font->GetTextWidthInternal(text.begin(), text.end()) * g_graphicsContext.GetGUIScaleX(); +} + +float CGUIFont::GetCharWidth( character_t ch ) +{ + if (!m_font) return 0; + CSingleLock lock(g_graphicsContext); + return m_font->GetCharWidthInternal(ch) * g_graphicsContext.GetGUIScaleX(); +} + +float CGUIFont::GetTextHeight(int numLines) const +{ + if (!m_font) return 0; + return m_font->GetTextHeight(m_lineSpacing, numLines) * g_graphicsContext.GetGUIScaleY(); +} + +float CGUIFont::GetLineHeight() const +{ + if (!m_font) return 0; + return m_font->GetLineHeight(m_lineSpacing) * g_graphicsContext.GetGUIScaleY(); +} + +void CGUIFont::Begin() +{ + if (!m_font) return; + m_font->Begin(); +} + +void CGUIFont::End() +{ + if (!m_font) return; + m_font->End(); +} + +void CGUIFont::SetFont(CGUIFontTTFBase *font) +{ + if (m_font == font) + return; // no need to update the font if we already have it + if (m_font) + m_font->RemoveReference(); + m_font = font; + if (m_font) + m_font->AddReference(); +} diff --git a/guilib/GUIFont.h b/guilib/GUIFont.h new file mode 100644 index 0000000000..88d996053f --- /dev/null +++ b/guilib/GUIFont.h @@ -0,0 +1,142 @@ +/*! +\file GUIFont.h +\brief +*/ + +#ifndef CGUILIB_GUIFONT_H +#define CGUILIB_GUIFONT_H +#pragma once + +#include "StdString.h" +#include <assert.h> + +typedef uint32_t character_t; +typedef uint32_t color_t; +typedef std::vector<character_t> vecText; +typedef std::vector<color_t> vecColors; + +class CGUIFontTTFBase; + +// flags for alignment +#define XBFONT_LEFT 0x00000000 +#define XBFONT_RIGHT 0x00000001 +#define XBFONT_CENTER_X 0x00000002 +#define XBFONT_CENTER_Y 0x00000004 +#define XBFONT_TRUNCATED 0x00000008 +#define XBFONT_JUSTIFIED 0x00000010 + +#define FONT_STYLE_NORMAL 0 +#define FONT_STYLE_BOLD 1 +#define FONT_STYLE_ITALICS 2 +#define FONT_STYLE_BOLD_ITALICS 3 + +class CScrollInfo +{ +public: + CScrollInfo(unsigned int wait = 50, float pos = 0, int speed = defaultSpeed, const CStdStringW &scrollSuffix = L" | ") + { + initialWait = wait; + initialPos = pos; + SetSpeed(speed); + suffix = scrollSuffix; + Reset(); + }; + void SetSpeed(int speed) + { + pixelSpeed = speed * 0.001f; + } + void Reset() + { + waitTime = initialWait; + characterPos = 0; + // pixelPos is where we start the current letter, so is measured + // to the left of the text rendering's left edge. Thus, a negative + // value will mean the text starts to the right + pixelPos = -initialPos; + // privates: + m_averageFrameTime = 1000.f / abs(defaultSpeed); + m_lastFrameTime = 0; + } + uint32_t GetCurrentChar(const vecText &text) const + { + assert(text.size()); + if (characterPos < text.size()) + return text[characterPos]; + else if (characterPos < text.size() + suffix.size()) + return suffix[characterPos - text.size()]; + return text[0]; + } + float GetPixelsPerFrame(); + + float pixelPos; + float pixelSpeed; + unsigned int waitTime; + unsigned int characterPos; + unsigned int initialWait; + float initialPos; + CStdStringW suffix; + + static const int defaultSpeed = 60; +private: + float m_averageFrameTime; + uint32_t m_lastFrameTime; +}; + +/*! + \ingroup textures + \brief + */ +class CGUIFont +{ +public: + CGUIFont(const CStdString& strFontName, uint32_t style, color_t textColor, color_t shadowColor, float lineSpacing, CGUIFontTTFBase *font); + virtual ~CGUIFont(); + + CStdString& GetFontName(); + + void DrawText( float x, float y, color_t color, color_t shadowColor, + const vecText &text, uint32_t alignment, float maxPixelWidth) + { + vecColors colors; + colors.push_back(color); + DrawText(x, y, colors, shadowColor, text, alignment, maxPixelWidth); + }; + + void DrawText( float x, float y, const vecColors &colors, color_t shadowColor, + const vecText &text, uint32_t alignment, float maxPixelWidth); + + void DrawScrollingText( float x, float y, const vecColors &colors, color_t shadowColor, + const vecText &text, uint32_t alignment, float maxPixelWidth, CScrollInfo &scrollInfo); + + float GetTextWidth( const vecText &text ); + float GetCharWidth( character_t ch ); + float GetTextHeight(int numLines) const; + float GetLineHeight() const; + + void Begin(); + void End(); + + uint32_t GetStyle() const { return m_style; }; + + static wchar_t RemapGlyph(wchar_t letter); + + CGUIFontTTFBase* GetFont() const + { + return m_font; + } + + void SetFont(CGUIFontTTFBase* font); + +protected: + CStdString m_strFontName; + uint32_t m_style; + color_t m_shadowColor; + color_t m_textColor; + float m_lineSpacing; + CGUIFontTTFBase *m_font; // the font object has the size information + +private: + bool ClippedRegionIsEmpty(float x, float y, float width, uint32_t alignment) const; +}; + +#endif diff --git a/guilib/GUIFontManager.cpp b/guilib/GUIFontManager.cpp new file mode 100644 index 0000000000..6a011daf84 --- /dev/null +++ b/guilib/GUIFontManager.cpp @@ -0,0 +1,484 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIFontManager.h" +#include "GraphicContext.h" +#include "SkinInfo.h" +#include "GUIFontTTF.h" +#include "GUIFont.h" +#include "XMLUtils.h" +#include "GUIControlFactory.h" +#include "../xbmc/Util.h" +#include "../xbmc/FileSystem/File.h" +#include "../xbmc/FileSystem/SpecialProtocol.h" +#include "utils/log.h" +#include "WindowingFactory.h" + +using namespace std; + +GUIFontManager g_fontManager; + +GUIFontManager::GUIFontManager(void) +{ + m_skinResolution=RES_INVALID; + m_fontsetUnicode=false; + m_bFontsNeedReloading = false; +} + +GUIFontManager::~GUIFontManager(void) +{ + Clear(); +} + +CGUIFont* GUIFontManager::LoadTTF(const CStdString& strFontName, const CStdString& strFilename, color_t textColor, color_t shadowColor, const int iSize, const int iStyle, float lineSpacing, float aspect, RESOLUTION sourceRes) +{ + float originalAspect = aspect; + + //check if font already exists + CGUIFont* pFont = GetFont(strFontName, false); + if (pFont) + return pFont; + + if (sourceRes == RES_INVALID) // no source res specified, so assume the skin res + sourceRes = m_skinResolution; + + + // set scaling resolution so that we can scale our font sizes correctly + // as fonts aren't scaled at render time (due to aliasing) we must scale + // the size of the fonts before they are drawn to bitmaps + g_graphicsContext.SetScalingResolution(sourceRes, 0, 0, true); + + // adjust aspect ratio + if (sourceRes == RES_PAL_16x9 || sourceRes == RES_PAL60_16x9 || sourceRes == RES_NTSC_16x9 || sourceRes == RES_HDTV_480p_16x9) + aspect *= 0.75f; + + aspect *= g_graphicsContext.GetGUIScaleY() / g_graphicsContext.GetGUIScaleX(); + float newSize = (float) iSize / g_graphicsContext.GetGUIScaleY(); + + // First try to load the font from the skin + CStdString strPath; + if (!CURL::IsFullPath(strFilename)) + { + strPath = CUtil::AddFileToFolder(g_graphicsContext.GetMediaDir(), "fonts"); + strPath = CUtil::AddFileToFolder(strPath, strFilename); + } + else + strPath = strFilename; + +#ifdef _LINUX + strPath = PTH_IC(strPath); +#endif + + // Check if the file exists, otherwise try loading it from the global media dir + if (!XFILE::CFile::Exists(strPath)) + { + strPath = CUtil::AddFileToFolder("special://xbmc/media/Fonts", CUtil::GetFileName(strFilename)); +#ifdef _LINUX + strPath = PTH_IC(strPath); +#endif + } + + // check if we already have this font file loaded (font object could differ only by color or style) + CStdString TTFfontName; + TTFfontName.Format("%s_%f_%f", strFilename, newSize, aspect); + + CGUIFontTTFBase* pFontFile = GetFontFile(TTFfontName); + if (!pFontFile) + { + pFontFile = new CGUIFontTTF(TTFfontName); + bool bFontLoaded = pFontFile->Load(strPath, newSize, aspect); + + if (!bFontLoaded) + { + delete pFontFile; + + // font could not be loaded - try Arial.ttf, which we distribute + if (strFilename != "arial.ttf") + { + CLog::Log(LOGERROR, "Couldn't load font name: %s(%s), trying to substitute arial.ttf", strFontName.c_str(), strFilename.c_str()); + return LoadTTF(strFontName, "arial.ttf", textColor, shadowColor, iSize, iStyle, lineSpacing, originalAspect); + } + CLog::Log(LOGERROR, "Couldn't load font name:%s file:%s", strFontName.c_str(), strPath.c_str()); + + return NULL; + } + + m_vecFontFiles.push_back(pFontFile); + } + + // font file is loaded, create our CGUIFont + CGUIFont *pNewFont = new CGUIFont(strFontName, iStyle, textColor, shadowColor, lineSpacing, pFontFile); + m_vecFonts.push_back(pNewFont); + + // Store the original TTF font info in case we need to reload it in a different resolution + OrigFontInfo fontInfo; + fontInfo.size = iSize; + fontInfo.aspect = originalAspect; + fontInfo.fontFilePath = strPath; + fontInfo.fileName = strFilename; + m_vecFontInfo.push_back(fontInfo); + + return pNewFont; +} + +void GUIFontManager::ReloadTTFFonts(void) +{ + if (!m_vecFonts.size()) + return; // we haven't even loaded fonts in yet + + // check if the device is ready + if(g_Windowing.GetDeviceStatus() != S_OK) + { + m_bFontsNeedReloading = true; + return; + } + + g_graphicsContext.SetScalingResolution(m_skinResolution, 0, 0, true); + + for (unsigned int i = 0; i < m_vecFonts.size(); i++) + { + CGUIFont* font = m_vecFonts[i]; + OrigFontInfo fontInfo = m_vecFontInfo[i]; + + float aspect = fontInfo.aspect; + int iSize = fontInfo.size; + CStdString& strPath = fontInfo.fontFilePath; + CStdString& strFilename = fontInfo.fileName; + + if (m_skinResolution == RES_PAL_16x9 || m_skinResolution == RES_PAL60_16x9 || m_skinResolution == RES_NTSC_16x9 || m_skinResolution == RES_HDTV_480p_16x9) + aspect *= 0.75f; + + aspect *= g_graphicsContext.GetGUIScaleY() / g_graphicsContext.GetGUIScaleX(); + float newSize = (float) iSize / g_graphicsContext.GetGUIScaleY(); + + CStdString TTFfontName; + TTFfontName.Format("%s_%f_%f", strFilename, newSize, aspect); + CGUIFontTTFBase* pFontFile = GetFontFile(TTFfontName); + if (!pFontFile) + { + pFontFile = new CGUIFontTTF(TTFfontName); + if (!pFontFile || !pFontFile->Load(strPath, newSize, aspect)) + { + delete pFontFile; + // font could not be loaded + CLog::Log(LOGERROR, "Couldn't re-load font file:%s", strPath.c_str()); + return; + } + + m_vecFontFiles.push_back(pFontFile); + } + + font->SetFont(pFontFile); + } + + m_bFontsNeedReloading = false; + // send a message to our controls telling them they need to refresh. + g_graphicsContext.SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_INVALIDATE); +} + +void GUIFontManager::Unload(const CStdString& strFontName) +{ + for (vector<CGUIFont*>::iterator iFont = m_vecFonts.begin(); iFont != m_vecFonts.end(); ++iFont) + { + if ((*iFont)->GetFontName() == strFontName) + { + delete (*iFont); + m_vecFonts.erase(iFont); + return; + } + } +} + +void GUIFontManager::FreeFontFile(CGUIFontTTFBase *pFont) +{ + for (vector<CGUIFontTTFBase*>::iterator it = m_vecFontFiles.begin(); it != m_vecFontFiles.end(); ++it) + { + if (pFont == *it) + { + m_vecFontFiles.erase(it); + delete pFont; + return; + } + } +} + +CGUIFontTTFBase* GUIFontManager::GetFontFile(const CStdString& strFileName) +{ + for (int i = 0; i < (int)m_vecFontFiles.size(); ++i) + { + CGUIFontTTFBase* pFont = (CGUIFontTTFBase *)m_vecFontFiles[i]; + if (pFont->GetFileName() == strFileName) + return pFont; + } + return NULL; +} + +CGUIFont* GUIFontManager::GetFont(const CStdString& strFontName, bool fallback /*= true*/) +{ + for (int i = 0; i < (int)m_vecFonts.size(); ++i) + { + CGUIFont* pFont = m_vecFonts[i]; + if (pFont->GetFontName() == strFontName) + return pFont; + } + // fall back to "font13" if we have none + if (fallback && !strFontName.IsEmpty() && !strFontName.Equals("-") && !strFontName.Equals("font13")) + return GetFont("font13"); + return NULL; +} + +void GUIFontManager::Clear() +{ + for (int i = 0; i < (int)m_vecFonts.size(); ++i) + { + CGUIFont* pFont = m_vecFonts[i]; + delete pFont; + } + + m_vecFonts.clear(); + m_vecFontFiles.clear(); + m_vecFontInfo.clear(); + m_fontsetUnicode=false; +} + +void GUIFontManager::LoadFonts(const CStdString& strFontSet) +{ + TiXmlDocument xmlDoc; + if (!OpenFontFile(xmlDoc)) + return; + + TiXmlElement* pRootElement = xmlDoc.RootElement(); + const TiXmlNode *pChild = pRootElement->FirstChild(); + + // If there are no fontset's defined in the XML (old skin format) run in backward compatibility + // and ignore the fontset request + CStdString strValue = pChild->Value(); + if (strValue == "fontset") + { + CStdString foundTTF; + while (pChild) + { + strValue = pChild->Value(); + if (strValue == "fontset") + { + const char* idAttr = ((TiXmlElement*) pChild)->Attribute("id"); + + const char* unicodeAttr = ((TiXmlElement*) pChild)->Attribute("unicode"); + + if (foundTTF.IsEmpty() && idAttr != NULL && unicodeAttr != NULL && stricmp(unicodeAttr, "true") == 0) + foundTTF = idAttr; + + // Check if this is the fontset that we want + if (idAttr != NULL && stricmp(strFontSet.c_str(), idAttr) == 0) + { + m_fontsetUnicode=false; + // Check if this is the a ttf fontset + if (unicodeAttr != NULL && stricmp(unicodeAttr, "true") == 0) + m_fontsetUnicode=true; + + if (m_fontsetUnicode) + { + LoadFonts(pChild->FirstChild()); + break; + } + } + + } + + pChild = pChild->NextSibling(); + } + + // If no fontset was loaded + if (pChild == NULL) + { + CLog::Log(LOGWARNING, "file doesnt have <fontset> with name '%s', defaulting to first fontset", strFontSet.c_str()); + if (!foundTTF.IsEmpty()) + LoadFonts(foundTTF); + } + } + else + { + CLog::Log(LOGERROR, "file doesnt have <fontset> in <fonts>, but rather %s", strValue.c_str()); + return ; + } +} + +void GUIFontManager::LoadFonts(const TiXmlNode* fontNode) +{ + while (fontNode) + { + CStdString strValue = fontNode->Value(); + if (strValue == "font") + { + const TiXmlNode *pNode = fontNode->FirstChild("name"); + if (pNode) + { + CStdString strFontName = pNode->FirstChild()->Value(); + color_t shadowColor = 0; + color_t textColor = 0; + CGUIControlFactory::GetColor(fontNode, "shadow", shadowColor); + CGUIControlFactory::GetColor(fontNode, "color", textColor); + const TiXmlNode *pNode = fontNode->FirstChild("filename"); + if (pNode) + { + CStdString strFontFileName = pNode->FirstChild()->Value(); + if (strFontFileName.Find(".ttf") >= 0) + { + int iSize = 20; + int iStyle = FONT_STYLE_NORMAL; + float aspect = 1.0f; + float lineSpacing = 1.0f; + + XMLUtils::GetInt(fontNode, "size", iSize); + if (iSize <= 0) iSize = 20; + + pNode = fontNode->FirstChild("style"); + if (pNode) + { + CStdString style = pNode->FirstChild()->Value(); + iStyle = FONT_STYLE_NORMAL; + if (style == "bold") + iStyle = FONT_STYLE_BOLD; + else if (style == "italics") + iStyle = FONT_STYLE_ITALICS; + else if (style == "bolditalics") + iStyle = FONT_STYLE_BOLD_ITALICS; + } + + XMLUtils::GetFloat(fontNode, "linespacing", lineSpacing); + XMLUtils::GetFloat(fontNode, "aspect", aspect); + + LoadTTF(strFontName, strFontFileName, textColor, shadowColor, iSize, iStyle, lineSpacing, aspect); + } + } + } + } + + fontNode = fontNode->NextSibling(); + } +} + +bool GUIFontManager::OpenFontFile(TiXmlDocument& xmlDoc) +{ + // Get the file to load fonts from: + CStdString strPath = g_SkinInfo.GetSkinPath("Font.xml", &m_skinResolution); + CLog::Log(LOGINFO, "Loading fonts from %s", strPath.c_str()); + + // first try our preferred file + if ( !xmlDoc.LoadFile(strPath) ) + { + CLog::Log(LOGERROR, "Couldn't load %s", strPath.c_str()); + return false; + } + TiXmlElement* pRootElement = xmlDoc.RootElement(); + + CStdString strValue = pRootElement->Value(); + if (strValue != CStdString("fonts")) + { + CLog::Log(LOGERROR, "file %s doesnt start with <fonts>", strPath.c_str()); + return false; + } + + return true; +} + +bool GUIFontManager::GetFirstFontSetUnicode(CStdString& strFontSet) +{ + strFontSet.Empty(); + + // Load our font file + TiXmlDocument xmlDoc; + if (!OpenFontFile(xmlDoc)) + return false; + + TiXmlElement* pRootElement = xmlDoc.RootElement(); + const TiXmlNode *pChild = pRootElement->FirstChild(); + + CStdString strValue = pChild->Value(); + if (strValue == "fontset") + { + while (pChild) + { + strValue = pChild->Value(); + if (strValue == "fontset") + { + const char* idAttr = ((TiXmlElement*) pChild)->Attribute("id"); + + const char* unicodeAttr = ((TiXmlElement*) pChild)->Attribute("unicode"); + + // Check if this is a fontset with a ttf attribute set to true + if (unicodeAttr != NULL && stricmp(unicodeAttr, "true") == 0) + { + // This is the first ttf fontset + strFontSet=idAttr; + break; + } + + } + + pChild = pChild->NextSibling(); + } + + // If no fontset was loaded + if (pChild == NULL) + CLog::Log(LOGWARNING, "file doesnt have <fontset> with with attibute unicode=\"true\""); + } + else + { + CLog::Log(LOGERROR, "file doesnt have <fontset> in <fonts>, but rather %s", strValue.c_str()); + } + + return !strFontSet.IsEmpty(); +} + +bool GUIFontManager::IsFontSetUnicode(const CStdString& strFontSet) +{ + TiXmlDocument xmlDoc; + if (!OpenFontFile(xmlDoc)) + return false; + + TiXmlElement* pRootElement = xmlDoc.RootElement(); + const TiXmlNode *pChild = pRootElement->FirstChild(); + + CStdString strValue = pChild->Value(); + if (strValue == "fontset") + { + while (pChild) + { + strValue = pChild->Value(); + if (strValue == "fontset") + { + const char* idAttr = ((TiXmlElement*) pChild)->Attribute("id"); + + const char* unicodeAttr = ((TiXmlElement*) pChild)->Attribute("unicode"); + + // Check if this is the fontset that we want + if (idAttr != NULL && stricmp(strFontSet.c_str(), idAttr) == 0) + return (unicodeAttr != NULL && stricmp(unicodeAttr, "true") == 0); + + } + + pChild = pChild->NextSibling(); + } + } + + return false; +} diff --git a/guilib/GUIFontManager.h b/guilib/GUIFontManager.h new file mode 100644 index 0000000000..9c4e847883 --- /dev/null +++ b/guilib/GUIFontManager.h @@ -0,0 +1,89 @@ +/*! +\file GUIFontManager.h +\brief +*/ + +#ifndef GUILIB_FONTMANAGER_H +#define GUILIB_FONTMANAGER_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GraphicContext.h" + +// Forward +class CGUIFont; +class CGUIFontTTFBase; +class TiXmlDocument; +class TiXmlNode; + +struct OrigFontInfo +{ + int size; + float aspect; + CStdString fontFilePath; + CStdString fileName; +}; + +/*! + \ingroup textures + \brief + */ +class GUIFontManager +{ +public: + GUIFontManager(void); + virtual ~GUIFontManager(void); + void Unload(const CStdString& strFontName); + void LoadFonts(const CStdString& strFontSet); + CGUIFont* LoadTTF(const CStdString& strFontName, const CStdString& strFilename, color_t textColor, color_t shadowColor, const int iSize, const int iStyle, float lineSpacing = 1.0f, float aspect = 1.0f, RESOLUTION res = RES_INVALID); + CGUIFont* GetFont(const CStdString& strFontName, bool fallback = true); + void Clear(); + void FreeFontFile(CGUIFontTTFBase *pFont); + + bool IsFontSetUnicode() { return m_fontsetUnicode; } + bool IsFontSetUnicode(const CStdString& strFontSet); + bool GetFirstFontSetUnicode(CStdString& strFontSet); + bool FontsNeedReloading() { return m_bFontsNeedReloading; } + + void ReloadTTFFonts(void); + +protected: + void LoadFonts(const TiXmlNode* fontNode); + CGUIFontTTFBase* GetFontFile(const CStdString& strFontFile); + bool OpenFontFile(TiXmlDocument& xmlDoc); + + std::vector<CGUIFont*> m_vecFonts; + std::vector<CGUIFontTTFBase*> m_vecFontFiles; + std::vector<OrigFontInfo> m_vecFontInfo; + bool m_fontsetUnicode; + RESOLUTION m_skinResolution; + bool m_bFontsNeedReloading; +}; + +/*! + \ingroup textures + \brief + */ +extern GUIFontManager g_fontManager; +#endif diff --git a/guilib/GUIFontTTF.cpp b/guilib/GUIFontTTF.cpp new file mode 100644 index 0000000000..91dd551bcf --- /dev/null +++ b/guilib/GUIFontTTF.cpp @@ -0,0 +1,772 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIFont.h" +#include "GUIFontTTF.h" +#include "GUIFontManager.h" +#include "Texture.h" +#include "GraphicContext.h" +#include "FileSystem/SpecialProtocol.h" +#include "MathUtils.h" +#include "utils/log.h" + +#include <math.h> + +// stuff for freetype +#ifndef _LINUX +#include "ft2build.h" +#else +#include <ft2build.h> +#endif +#include FT_FREETYPE_H +#include FT_GLYPH_H +#include FT_OUTLINE_H + +#define USE_RELEASE_LIBS + +using namespace std; + + + +// our free type library (debug) +#if defined(_DEBUG) && !defined(USE_RELEASE_LIBS) + #pragma comment (lib,"../../guilib/freetype2/freetype239ST_D.lib") +#elif !defined(__GNUC__) + #pragma comment (lib,"../../guilib/freetype2/freetype239ST.lib") +#endif + + +#define CHARS_PER_TEXTURE_LINE 20 // number of characters to cache per texture line +#define CHAR_CHUNK 64 // 64 chars allocated at a time (1024 bytes) + +int CGUIFontTTFBase::justification_word_weight = 6; // weight of word spacing over letter spacing when justifying. + // A larger number means more of the "dead space" is placed between + // words rather than between letters. + +unsigned int CGUIFontTTFBase::max_texture_size = 2048; // max texture size - 2048 for GMA965 + +class CFreeTypeLibrary +{ +public: + CFreeTypeLibrary() + { + m_library = NULL; + } + + virtual ~CFreeTypeLibrary() + { + if (m_library) + FT_Done_FreeType(m_library); + } + + FT_Face GetFont(const CStdString &filename, float size, float aspect) + { + // don't have it yet - create it + if (!m_library) + FT_Init_FreeType(&m_library); + if (!m_library) + { + CLog::Log(LOGERROR, "Unable to initialize freetype library"); + return NULL; + } + + FT_Face face; + + // ok, now load the font face + if (FT_New_Face( m_library, _P(filename).c_str(), 0, &face )) + return NULL; + + unsigned int ydpi = GetDPI(); + unsigned int xdpi = (unsigned int)MathUtils::round_int(ydpi * aspect); + + // we set our screen res currently to 96dpi in both directions (windows default) + // we cache our characters (for rendering speed) so it's probably + // not a good idea to allow free scaling of fonts - rather, just + // scaling to pixel ratio on screen perhaps? + if (FT_Set_Char_Size( face, 0, (int)(size*64 + 0.5f), xdpi, ydpi )) + { + FT_Done_Face(face); + return NULL; + } + + return face; + }; + + void ReleaseFont(FT_Face face) + { + assert(face); + FT_Done_Face(face); + }; + + unsigned int GetDPI() const + { + return 72; // default dpi, matches what XPR fonts used to use for sizing + }; + +private: + FT_Library m_library; +}; + +CFreeTypeLibrary g_freeTypeLibrary; // our freetype library + +CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName) +{ + m_texture = NULL; + m_char = NULL; + m_maxChars = 0; + m_nestedBeginCount = 0; + + m_bTextureLoaded = false; + m_vertex_size = 4*1024; + m_vertex = (SVertex*)malloc(m_vertex_size * sizeof(SVertex)); + + m_face = NULL; + memset(m_charquick, 0, sizeof(m_charquick)); + m_strFileName = strFileName; + m_referenceCount = 0; + m_originX = m_originY = 0.0f; + m_cellBaseLine = m_cellHeight = 0; + m_numChars = 0; + m_posX = m_posY = 0; + m_textureHeight = m_textureWidth = 0; + m_textureScaleX = m_textureScaleY = 0.0; + m_ellipsesWidth = m_height = 0.0f; + m_color = 0; + m_vertex_count = 0; + m_nTexture = 0; +} + +CGUIFontTTFBase::~CGUIFontTTFBase(void) +{ + Clear(); +} + +void CGUIFontTTFBase::AddReference() +{ + m_referenceCount++; +} + +void CGUIFontTTFBase::RemoveReference() +{ + // delete this object when it's reference count hits zero + m_referenceCount--; + if (!m_referenceCount) + g_fontManager.FreeFontFile(this); +} + + +void CGUIFontTTFBase::ClearCharacterCache() +{ + if (m_texture) + { + delete(m_texture); + } + + DeleteHardwareTexture(); + + m_texture = NULL; + if (m_char) + delete[] m_char; + m_char = new Character[CHAR_CHUNK]; + memset(m_charquick, 0, sizeof(m_charquick)); + m_numChars = 0; + m_maxChars = CHAR_CHUNK; + // set the posX and posY so that our texture will be created on first character write. + m_posX = m_textureWidth; + m_posY = -(int)m_cellHeight; +} + +void CGUIFontTTFBase::Clear() +{ + if (m_texture) + delete(m_texture); + + m_texture = NULL; + if (m_char) + delete[] m_char; + memset(m_charquick, 0, sizeof(m_charquick)); + m_char = NULL; + m_maxChars = 0; + m_numChars = 0; + m_posX = 0; + m_posY = 0; + m_nestedBeginCount = 0; + + if (m_face) + g_freeTypeLibrary.ReleaseFont(m_face); + m_face = NULL; + + free(m_vertex); + m_vertex = NULL; + m_vertex_count = 0; +} + +bool CGUIFontTTFBase::Load(const CStdString& strFilename, float height, float aspect, float lineSpacing) +{ + // we now know that this object is unique - only the GUIFont objects are non-unique, so no need + // for reference tracking these fonts + m_face = g_freeTypeLibrary.GetFont(strFilename, height, aspect); + + if (!m_face) + return false; + + // grab the maximum cell height and width + unsigned int m_cellWidth = m_face->bbox.xMax - m_face->bbox.xMin; + m_cellHeight = m_face->bbox.yMax - m_face->bbox.yMin; + m_cellBaseLine = m_face->bbox.yMax; + + unsigned int ydpi = g_freeTypeLibrary.GetDPI(); + unsigned int xdpi = (unsigned int)MathUtils::round_int(ydpi * aspect); + + m_cellWidth *= (unsigned int)(height * xdpi); + m_cellWidth /= (72 * m_face->units_per_EM); + + m_cellHeight *= (unsigned int)(height * ydpi); + m_cellHeight /= (72 * m_face->units_per_EM); + + m_cellBaseLine *= (unsigned int)(height * ydpi); + m_cellBaseLine /= (72 * m_face->units_per_EM); + + // increment for good measure to give space in our texture + m_cellWidth++; + m_cellHeight+=2; + m_cellBaseLine++; + +// CLog::Log(LOGDEBUG, "%s Scaled size of font %s (%f): width = %i, height = %i, lineheight = %li", +// __FUNCTION__, strFilename.c_str(), height, m_cellWidth, m_cellHeight, m_face->size->metrics.height / 64); + + m_height = height; + + if (m_texture) + delete(m_texture); + + m_texture = NULL; + if (m_char) + delete[] m_char; + m_char = NULL; + + m_maxChars = 0; + m_numChars = 0; + + m_strFilename = strFilename; + + m_textureHeight = 0; + m_textureWidth = ((m_cellHeight * CHARS_PER_TEXTURE_LINE) & ~63) + 64; + + m_textureWidth = CBaseTexture::PadPow2(m_textureWidth); + + if (m_textureWidth > max_texture_size) m_textureWidth = max_texture_size; + + // set the posX and posY so that our texture will be created on first character write. + m_posX = m_textureWidth; + m_posY = -(int)m_cellHeight; + + // cache the ellipses width + Character *ellipse = GetCharacter(L'.'); + if (ellipse) m_ellipsesWidth = ellipse->advance; + + return true; +} + +void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors, const vecText &text, uint32_t alignment, float maxPixelWidth, bool scrolling) +{ + Begin(); + + // save the origin, which is scaled separately + m_originX = x; + m_originY = y; + + // Check if we will really need to truncate or justify the text + if ( alignment & XBFONT_TRUNCATED ) + { + if ( maxPixelWidth <= 0.0f || GetTextWidthInternal(text.begin(), text.end()) <= maxPixelWidth) + alignment &= ~XBFONT_TRUNCATED; + } + else if ( alignment & XBFONT_JUSTIFIED ) + { + if ( maxPixelWidth <= 0.0f ) + alignment &= ~XBFONT_JUSTIFIED; + } + + // calculate sizing information + float startX = 0; + float startY = (alignment & XBFONT_CENTER_Y) ? -0.5f*(m_cellHeight-2) : 0; // vertical centering + + if ( alignment & (XBFONT_RIGHT | XBFONT_CENTER_X) ) + { + // Get the extent of this line + float w = GetTextWidthInternal( text.begin(), text.end() ); + + if ( alignment & XBFONT_TRUNCATED && w > maxPixelWidth ) + w = maxPixelWidth; + + if ( alignment & XBFONT_CENTER_X) + w *= 0.5f; + // Offset this line's starting position + startX -= w; + } + + float spacePerLetter = 0; // for justification effects + if ( alignment & XBFONT_JUSTIFIED ) + { + // first compute the size of the text to render in both characters and pixels + unsigned int lineChars = 0; + float linePixels = 0; + for (vecText::const_iterator pos = text.begin(); pos != text.end(); pos++) + { + Character *ch = GetCharacter(*pos); + if (ch) + { // spaces have multiple times the justification spacing of normal letters + lineChars += ((*pos & 0xffff) == L' ') ? justification_word_weight : 1; + linePixels += ch->advance; + } + } + if (lineChars > 1) + spacePerLetter = (maxPixelWidth - linePixels) / (lineChars - 1); + } + float cursorX = 0; // current position along the line + + for (vecText::const_iterator pos = text.begin(); pos != text.end(); pos++) + { + // If starting text on a new line, determine justification effects + // Get the current letter in the CStdString + color_t color = (*pos & 0xff0000) >> 16; + if (color >= colors.size()) + color = 0; + color = colors[color]; + + // grab the next character + Character *ch = GetCharacter(*pos); + if (!ch) continue; + + if ( alignment & XBFONT_TRUNCATED ) + { + // Check if we will be exceeded the max allowed width + if ( cursorX + ch->advance + 3 * m_ellipsesWidth > maxPixelWidth ) + { + // Yup. Let's draw the ellipses, then bail + // Perhaps we should really bail to the next line in this case?? + Character *period = GetCharacter(L'.'); + if (!period) + break; + + for (int i = 0; i < 3; i++) + { + RenderCharacter(startX + cursorX, startY, period, color, !scrolling); + cursorX += period->advance; + } + break; + } + } + else if (maxPixelWidth > 0 && cursorX > maxPixelWidth) + break; // exceeded max allowed width - stop rendering + + RenderCharacter(startX + cursorX, startY, ch, color, !scrolling); + if ( alignment & XBFONT_JUSTIFIED ) + { + if ((*pos & 0xffff) == L' ') + cursorX += ch->advance + spacePerLetter * justification_word_weight; + else + cursorX += ch->advance + spacePerLetter; + } + else + cursorX += ch->advance; + } + + End(); +} + +// this routine assumes a single line (i.e. it was called from GUITextLayout) +float CGUIFontTTFBase::GetTextWidthInternal(vecText::const_iterator start, vecText::const_iterator end) +{ + float width = 0; + while (start != end) + { + Character *c = GetCharacter(*start++); + if (c) width += c->advance; + } + return width; +} + +float CGUIFontTTFBase::GetCharWidthInternal(character_t ch) +{ + Character *c = GetCharacter(ch); + if (c) return c->advance; + return 0; +} + +float CGUIFontTTFBase::GetTextHeight(float lineSpacing, int numLines) const +{ + return (float)(numLines - 1) * GetLineHeight(lineSpacing) + (m_cellHeight - 2); // -2 as we increment this for space in our texture +} + +float CGUIFontTTFBase::GetLineHeight(float lineSpacing) const +{ + if (m_face) + return lineSpacing * m_face->size->metrics.height / 64.0f; + return 0.0f; +} + +CGUIFontTTFBase::Character* CGUIFontTTFBase::GetCharacter(character_t chr) +{ + wchar_t letter = (wchar_t)(chr & 0xffff); + character_t style = (chr & 0x3000000) >> 24; + + // ignore linebreaks + if (letter == L'\r') + return NULL; + + // quick access to ascii chars + if (letter < 255) + { + character_t ch = (style << 8) | letter; + if (m_charquick[ch]) + return m_charquick[ch]; + } + + // letters are stored based on style and letter + character_t ch = (style << 16) | letter; + + int low = 0; + int high = m_numChars - 1; + int mid; + while (low <= high) + { + mid = (low + high) >> 1; + if (ch > m_char[mid].letterAndStyle) + low = mid + 1; + else if (ch < m_char[mid].letterAndStyle) + high = mid - 1; + else + return &m_char[mid]; + } + // if we get to here, then low is where we should insert the new character + + // increase the size of the buffer if we need it + if (m_numChars >= m_maxChars) + { // need to increase the size of the buffer + Character *newTable = new Character[m_maxChars + CHAR_CHUNK]; + if (m_char) + { + memcpy(newTable, m_char, low * sizeof(Character)); + memcpy(newTable + low + 1, m_char + low, (m_numChars - low) * sizeof(Character)); + delete[] m_char; + } + m_char = newTable; + m_maxChars += CHAR_CHUNK; + + } + else + { // just move the data along as necessary + memmove(m_char + low + 1, m_char + low, (m_numChars - low) * sizeof(Character)); + } + // render the character to our texture + // must End() as we can't render text to our texture during a Begin(), End() block + unsigned int nestedBeginCount = m_nestedBeginCount; + m_nestedBeginCount = 1; + if (nestedBeginCount) End(); + if (!CacheCharacter(letter, style, m_char + low)) + { // unable to cache character - try clearing them all out and starting over + CLog::Log(LOGDEBUG, "GUIFontTTF::GetCharacter: Unable to cache character. Clearing character cache of %i characters", m_numChars); + ClearCharacterCache(); + low = 0; + if (!CacheCharacter(letter, style, m_char + low)) + { + CLog::Log(LOGERROR, "GUIFontTTF::GetCharacter: Unable to cache character (out of memory?)"); + if (nestedBeginCount) Begin(); + m_nestedBeginCount = nestedBeginCount; + return NULL; + } + } + if (nestedBeginCount) Begin(); + m_nestedBeginCount = nestedBeginCount; + + // fixup quick access + memset(m_charquick, 0, sizeof(m_charquick)); + for(int i=0;i<m_numChars;i++) + { + if ((m_char[i].letterAndStyle & 0xffff) < 255) + { + character_t ch = ((m_char[i].letterAndStyle & 0xffff0000) >> 8) | (m_char[i].letterAndStyle & 0xff); + m_charquick[ch] = m_char+i; + } + } + + return m_char + low; +} + +bool CGUIFontTTFBase::CacheCharacter(wchar_t letter, uint32_t style, Character *ch) +{ + int glyph_index = FT_Get_Char_Index( m_face, letter ); + + FT_Glyph glyph = NULL; + if (FT_Load_Glyph( m_face, glyph_index, FT_LOAD_TARGET_LIGHT )) + { + CLog::Log(LOGDEBUG, "%s Failed to load glyph %x", __FUNCTION__, letter); + return false; + } + // make bold if applicable + if (style & FONT_STYLE_BOLD) + EmboldenGlyph(m_face->glyph); + // and italics if applicable + if (style & FONT_STYLE_ITALICS) + ObliqueGlyph(m_face->glyph); + // grab the glyph + if (FT_Get_Glyph(m_face->glyph, &glyph)) + { + CLog::Log(LOGDEBUG, "%s Failed to get glyph %x", __FUNCTION__, letter); + return false; + } + // render the glyph + if (FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, NULL, 1)) + { + CLog::Log(LOGDEBUG, "%s Failed to render glyph %x to a bitmap", __FUNCTION__, letter); + return false; + } + FT_BitmapGlyph bitGlyph = (FT_BitmapGlyph)glyph; + FT_Bitmap bitmap = bitGlyph->bitmap; + if (bitGlyph->left < 0) + m_posX += -bitGlyph->left; + + // check we have enough room for the character + if (m_posX + bitGlyph->left + bitmap.width > (int)m_textureWidth) + { // no space - gotta drop to the next line (which means creating a new texture and copying it across) + m_posX = 0; + m_posY += m_cellHeight; + if (bitGlyph->left < 0) + m_posX += -bitGlyph->left; + + if(m_posY + m_cellHeight >= m_textureHeight) + { + // create the new larger texture + unsigned int newHeight = m_posY + m_cellHeight; + // check for max height (can't be more than max_texture_size texels + if (newHeight > max_texture_size) + { + CLog::Log(LOGDEBUG, "GUIFontTTF::CacheCharacter: New cache texture is too large (%u > %u pixels long)", newHeight, max_texture_size); + FT_Done_Glyph(glyph); + return false; + } + + CBaseTexture* newTexture = NULL; + newTexture = ReallocTexture(newHeight); + if(newTexture == NULL) + { + FT_Done_Glyph(glyph); + return false; + } + m_texture = newTexture; + } + } + + // set the character in our table + ch->letterAndStyle = (style << 16) | letter; + ch->offsetX = (short)bitGlyph->left; + ch->offsetY = (short)max((short)m_cellBaseLine - bitGlyph->top, 0); + ch->left = (float)m_posX + ch->offsetX; + ch->top = (float)m_posY + ch->offsetY; + ch->right = ch->left + bitmap.width; + ch->bottom = ch->top + bitmap.rows; + ch->advance = (float)MathUtils::round_int( (float)m_face->glyph->advance.x / 64 ); + + // we need only render if we actually have some pixels + if (bitmap.width * bitmap.rows) + { + CopyCharToTexture(bitGlyph, ch); + } + m_posX += (unsigned short)max(ch->right - ch->left + ch->offsetX, ch->advance + 1); + m_numChars++; + + m_textureScaleX = 1.0f / m_textureWidth; + m_textureScaleY = 1.0f / m_textureHeight; + + // free the glyph + FT_Done_Glyph(glyph); + + return true; +} + +void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *ch, color_t color, bool roundX) +{ + // actual image width isn't same as the character width as that is + // just baseline width and height should include the descent + const float width = ch->right - ch->left; + const float height = ch->bottom - ch->top; + + // posX and posY are relative to our origin, and the textcell is offset + // from our (posX, posY). Plus, these are unscaled quantities compared to the underlying GUI resolution + CRect vertex((posX + ch->offsetX) * g_graphicsContext.GetGUIScaleX(), + (posY + ch->offsetY) * g_graphicsContext.GetGUIScaleY(), + (posX + ch->offsetX + width) * g_graphicsContext.GetGUIScaleX(), + (posY + ch->offsetY + height) * g_graphicsContext.GetGUIScaleY()); + vertex += CPoint(m_originX, m_originY); + CRect texture(ch->left, ch->top, ch->right, ch->bottom); + g_graphicsContext.ClipRect(vertex, texture); + + // transform our positions - note, no scaling due to GUI calibration/resolution occurs + float x[4]; + + x[0] = g_graphicsContext.ScaleFinalXCoord(vertex.x1, vertex.y1); + x[1] = g_graphicsContext.ScaleFinalXCoord(vertex.x2, vertex.y1); + x[2] = g_graphicsContext.ScaleFinalXCoord(vertex.x2, vertex.y2); + x[3] = g_graphicsContext.ScaleFinalXCoord(vertex.x1, vertex.y2); + + if (roundX) + { + // We only round the "left" side of the character, and then use the direction of rounding to + // move the "right" side of the character. This ensures that a constant width is kept when rendering + // the same letter at the same size at different places of the screen, avoiding the problem + // of the "left" side rounding one way while the "right" side rounds the other way, thus getting + // altering the width of thin characters substantially. This only really works for positive + // coordinates (due to the direction of truncation for negatives) but this is the only case that + // really interests us anyway. + float rx0 = (float)MathUtils::round_int(x[0]); + float rx3 = (float)MathUtils::round_int(x[3]); + x[1] = (float)MathUtils::truncate_int(x[1]); + x[2] = (float)MathUtils::truncate_int(x[2]); + if (rx0 > x[0]) + x[1] += 1; + if (rx3 > x[3]) + x[2] += 1; + x[0] = rx0; + x[3] = rx3; + } + + float y1 = (float)MathUtils::round_int(g_graphicsContext.ScaleFinalYCoord(vertex.x1, vertex.y1)); + float y2 = (float)MathUtils::round_int(g_graphicsContext.ScaleFinalYCoord(vertex.x2, vertex.y1)); + float y3 = (float)MathUtils::round_int(g_graphicsContext.ScaleFinalYCoord(vertex.x2, vertex.y2)); + float y4 = (float)MathUtils::round_int(g_graphicsContext.ScaleFinalYCoord(vertex.x1, vertex.y2)); + + float z1 = (float)MathUtils::round_int(g_graphicsContext.ScaleFinalZCoord(vertex.x1, vertex.y1)); + float z2 = (float)MathUtils::round_int(g_graphicsContext.ScaleFinalZCoord(vertex.x2, vertex.y1)); + float z3 = (float)MathUtils::round_int(g_graphicsContext.ScaleFinalZCoord(vertex.x2, vertex.y2)); + float z4 = (float)MathUtils::round_int(g_graphicsContext.ScaleFinalZCoord(vertex.x1, vertex.y2)); + + // tex coords converted to 0..1 range + float tl = texture.x1 * m_textureScaleX; + float tr = texture.x2 * m_textureScaleX; + float tt = texture.y1 * m_textureScaleY; + float tb = texture.y2 * m_textureScaleY; + + // grow the vertex buffer if required + if(m_vertex_count >= m_vertex_size) + { + m_vertex_size *= 2; + m_vertex = (SVertex*)realloc(m_vertex, m_vertex_size * sizeof(SVertex)); + } + + m_color = color; + SVertex* v = m_vertex + m_vertex_count; + + for(int i = 0; i < 4; i++) + { + v[i].r = GET_R(color); + v[i].g = GET_G(color); + v[i].b = GET_B(color); + v[i].a = GET_A(color); + } + + v[0].u = tl; + v[0].v = tt; + v[0].x = x[0]; + v[0].y = y1; + v[0].z = z1; + + v[1].u = tr; + v[1].v = tt; + v[1].x = x[1]; + v[1].y = y2; + v[1].z = z2; + + v[2].u = tr; + v[2].v = tb; + v[2].x = x[2]; + v[2].y = y3; + v[2].z = z3; + + v[3].u = tl; + v[3].v = tb; + v[3].x = x[3]; + v[3].y = y4; + v[3].z = z4; + + RenderInternal(v); + + m_vertex_count+=4; +} + +// Oblique code - original taken from freetype2 (ftsynth.c) +void CGUIFontTTFBase::ObliqueGlyph(FT_GlyphSlot slot) +{ + /* only oblique outline glyphs */ + if ( slot->format != FT_GLYPH_FORMAT_OUTLINE ) + return; + + /* we don't touch the advance width */ + + /* For italic, simply apply a shear transform, with an angle */ + /* of about 12 degrees. */ + + FT_Matrix transform; + transform.xx = 0x10000L; + transform.yx = 0x00000L; + + transform.xy = 0x06000L; + transform.yy = 0x10000L; + + FT_Outline_Transform( &slot->outline, &transform ); +} + + +// Embolden code - original taken from freetype2 (ftsynth.c) +void CGUIFontTTFBase::EmboldenGlyph(FT_GlyphSlot slot) +{ + if ( slot->format != FT_GLYPH_FORMAT_OUTLINE ) + return; + + /* some reasonable strength */ + FT_Pos strength = FT_MulFix( m_face->units_per_EM, + m_face->size->metrics.y_scale ) / 24; + + FT_BBox bbox_before, bbox_after; + FT_Outline_Get_CBox( &slot->outline, &bbox_before ); + FT_Outline_Embolden( &slot->outline, strength ); // ignore error + FT_Outline_Get_CBox( &slot->outline, &bbox_after ); + + FT_Pos dx = bbox_after.xMax - bbox_before.xMax; + FT_Pos dy = bbox_after.yMax - bbox_before.yMax; + + if ( slot->advance.x ) + slot->advance.x += dx; + + if ( slot->advance.y ) + slot->advance.y += dy; + + slot->metrics.width += dx; + slot->metrics.height += dy; + slot->metrics.horiBearingY += dy; + slot->metrics.horiAdvance += dx; + slot->metrics.vertBearingX -= dx / 2; + slot->metrics.vertBearingY += dy; + slot->metrics.vertAdvance += dy; +} + + diff --git a/guilib/GUIFontTTF.h b/guilib/GUIFontTTF.h new file mode 100644 index 0000000000..098fbe0c0a --- /dev/null +++ b/guilib/GUIFontTTF.h @@ -0,0 +1,182 @@ +/*! +\file GUIFont.h +\brief +*/ + +#ifndef CGUILIB_GUIFONTTTF_H +#define CGUILIB_GUIFONTTTF_H +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +// forward definition +class CBaseTexture; + +struct FT_FaceRec_; +struct FT_LibraryRec_; +struct FT_GlyphSlotRec_; +struct FT_BitmapGlyphRec_; + +typedef struct FT_FaceRec_ *FT_Face; +typedef struct FT_LibraryRec_ *FT_Library; +typedef struct FT_GlyphSlotRec_ *FT_GlyphSlot; +typedef struct FT_BitmapGlyphRec_ *FT_BitmapGlyph; + +typedef uint32_t character_t; +typedef uint32_t color_t; +typedef std::vector<character_t> vecText; +typedef std::vector<color_t> vecColors; + +/*! + \ingroup textures + \brief + */ + +typedef struct _SVertex +{ + float u, v; + unsigned char r, g, b, a; + float x, y, z; +} SVertex; + + +class CGUIFontTTFBase +{ + friend class CGUIFont; + +public: + + CGUIFontTTFBase(const CStdString& strFileName); + virtual ~CGUIFontTTFBase(void); + + void Clear(); + + bool Load(const CStdString& strFilename, float height = 20.0f, float aspect = 1.0f, float lineSpacing = 1.0f); + + virtual void Begin() = 0; + virtual void End() = 0; + + const CStdString& GetFileName() const { return m_strFileName; }; + +protected: + struct Character + { + short offsetX, offsetY; + float left, top, right, bottom; + float advance; + character_t letterAndStyle; + }; + void AddReference(); + void RemoveReference(); + + float GetTextWidthInternal(vecText::const_iterator start, vecText::const_iterator end); + float GetCharWidthInternal(character_t ch); + float GetTextHeight(float lineSpacing, int numLines) const; + float GetLineHeight(float lineSpacing) const; + + void DrawTextInternal(float x, float y, const vecColors &colors, const vecText &text, + uint32_t alignment, float maxPixelWidth, bool scrolling); + + void DrawTextInternal(float x, float y, color_t color, const vecText &text, + uint32_t alignment, float maxPixelWidth, bool scrolling) + { + vecColors colors; + colors.push_back(color); + DrawTextInternal(x, y, colors, text, alignment, maxPixelWidth, scrolling); + } + + float m_height; + CStdString m_strFilename; + + // Stuff for pre-rendering for speed + inline Character *GetCharacter(character_t letter); + bool CacheCharacter(wchar_t letter, uint32_t style, Character *ch); + void RenderCharacter(float posX, float posY, const Character *ch, color_t color, bool roundX); + void ClearCharacterCache(); + + virtual CBaseTexture* ReallocTexture(unsigned int& newHeight) = 0; + virtual bool CopyCharToTexture(FT_BitmapGlyph bitGlyph, Character *ch) = 0; + virtual void DeleteHardwareTexture() = 0; + virtual void RenderInternal(SVertex* v) = 0; + + // modifying glyphs + void EmboldenGlyph(FT_GlyphSlot slot); + void ObliqueGlyph(FT_GlyphSlot slot); + + CBaseTexture* m_texture; // texture that holds our rendered characters (8bit alpha only) + + unsigned int m_textureWidth; // width of our texture + unsigned int m_textureHeight; // heigth of our texture + int m_posX; // current position in the texture + int m_posY; + + color_t m_color; + + Character *m_char; // our characters + Character *m_charquick[256*4]; // ascii chars (4 styles) here + int m_maxChars; // size of character array (can be incremented) + int m_numChars; // the current number of cached characters + + float m_ellipsesWidth; // this is used every character (width of '.') + + unsigned int m_cellBaseLine; + unsigned int m_cellHeight; + + unsigned int m_nestedBeginCount; // speedups + + // freetype stuff + FT_Face m_face; + + float m_originX; + float m_originY; + + bool m_bTextureLoaded; + unsigned int m_nTexture; + + SVertex* m_vertex; + int m_vertex_count; + int m_vertex_size; + + float m_textureScaleX; + float m_textureScaleY; + + static int justification_word_weight; + static unsigned int max_texture_size; + + CStdString m_strFileName; + +private: + int m_referenceCount; +}; + +#if defined(HAS_GL) +#include "GUIFontTTFGL.h" +#define CGUIFontTTF CGUIFontTTFGL +#elif defined(HAS_GLES) +#include "GUIFontTTFGLES.h" +#define CGUIFontTTF CGUIFontTTFGLES +#elif defined(HAS_DX) +#include "GUIFontTTFDX.h" +#define CGUIFontTTF CGUIFontTTFDX +#endif + +#endif diff --git a/guilib/GUIFontTTFDX.cpp b/guilib/GUIFontTTFDX.cpp new file mode 100644 index 0000000000..85e11ab867 --- /dev/null +++ b/guilib/GUIFontTTFDX.cpp @@ -0,0 +1,196 @@ +/*
+ * Copyright (C) 2005-2008 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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, 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 XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIFont.h"
+#include "GUIFontTTFDX.h"
+#include "GUIFontManager.h"
+#include "Texture.h"
+#include "gui3d.h"
+#include "WindowingFactory.h"
+
+// stuff for freetype
+#include "ft2build.h"
+
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+#include FT_OUTLINE_H
+
+using namespace std;
+
+#ifdef HAS_DX
+
+struct CUSTOMVERTEX
+{
+ FLOAT x, y, z;
+ DWORD color;
+ FLOAT tu, tv; // Texture coordinates
+};
+
+
+CGUIFontTTFDX::CGUIFontTTFDX(const CStdString& strFileName)
+: CGUIFontTTFBase(strFileName)
+{
+
+}
+
+CGUIFontTTFDX::~CGUIFontTTFDX(void)
+{
+
+}
+
+void CGUIFontTTFDX::RenderInternal(SVertex* v)
+{
+ CUSTOMVERTEX verts[4] = {
+ { v[0].x-0.5f, v[0].y-0.5f, v[0].z, m_color, v[0].u, v[0].v},
+ { v[1].x-0.5f, v[1].y-0.5f, v[1].z, m_color, v[1].u, v[1].v},
+ { v[2].x-0.5f, v[2].y-0.5f, v[2].z, m_color, v[2].u, v[2].v},
+ { v[3].x-0.5f, v[3].y-0.5f, v[3].z, m_color, v[3].u, v[3].v}
+ };
+
+ g_Windowing.Get3DDevice()->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, verts, sizeof(CUSTOMVERTEX));
+}
+
+void CGUIFontTTFDX::Begin()
+{
+ LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice();
+
+ if (m_nestedBeginCount == 0)
+ {
+ // just have to blit from our texture.
+ pD3DDevice->SetTexture( 0, m_texture->GetTextureObject() );
+
+ pD3DDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
+ pD3DDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP );
+ pD3DDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
+ pD3DDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
+ pD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 ); // only use diffuse
+ pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
+ pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
+ pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
+
+ // no other texture stages needed
+ pD3DDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+
+ pD3DDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
+ pD3DDevice->SetRenderState( D3DRS_FOGENABLE, FALSE );
+ pD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
+ pD3DDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
+ pD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
+ pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
+ pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
+ pD3DDevice->SetRenderState( D3DRS_LIGHTING, FALSE);
+
+ pD3DDevice->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);
+ }
+ // Keep track of the nested begin/end calls.
+ m_vertex_count = 0;
+ m_nestedBeginCount++;
+}
+
+void CGUIFontTTFDX::End()
+{
+ LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice();
+
+ if (m_nestedBeginCount == 0)
+ return;
+
+ if (--m_nestedBeginCount > 0)
+ return;
+
+ pD3DDevice->SetTexture(0, NULL);
+ pD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
+}
+
+CBaseTexture* CGUIFontTTFDX::ReallocTexture(unsigned int& newHeight)
+{
+ CBaseTexture* pNewTexture = new CTexture(m_textureWidth, newHeight, 8);
+
+ if(pNewTexture == NULL)
+ return NULL;
+
+ pNewTexture->CreateTextureObject();
+
+ LPDIRECT3DTEXTURE9 newTexture = pNewTexture->GetTextureObject();
+
+ if(newTexture == NULL)
+ return NULL;
+
+ // correct texture sizes
+ D3DSURFACE_DESC desc;
+ newTexture->GetLevelDesc(0, &desc);
+ m_textureHeight = desc.Height;
+ m_textureWidth = desc.Width;
+
+ D3DLOCKED_RECT rect;
+ newTexture->LockRect(0, &rect, NULL, 0);
+ memset(rect.pBits, 0, rect.Pitch * m_textureHeight);
+ newTexture->UnlockRect(0);
+
+ if (m_texture)
+ { // copy across from our current one using gpu
+ LPDIRECT3DSURFACE9 pTarget, pSource;
+ newTexture->GetSurfaceLevel(0, &pTarget);
+ m_texture->GetTextureObject()->GetSurfaceLevel(0, &pSource);
+
+ // TODO:DIRECTX - this is probably really slow, but UpdateSurface() doesn't like rendering from non-system textures
+ D3DXLoadSurfaceFromSurface(pTarget, NULL, NULL, pSource, NULL, NULL, D3DX_FILTER_NONE, 0);
+
+ SAFE_RELEASE(pTarget);
+ SAFE_RELEASE(pSource);
+ delete m_texture;
+ }
+
+ return pNewTexture;
+}
+
+bool CGUIFontTTFDX::CopyCharToTexture(FT_BitmapGlyph bitGlyph, Character* ch)
+{
+ FT_Bitmap bitmap = bitGlyph->bitmap;
+
+ // render this onto our normal texture using gpu
+ LPDIRECT3DSURFACE9 target;
+ m_texture->GetTextureObject()->GetSurfaceLevel(0, &target);
+
+ RECT sourcerect = { 0, 0, bitmap.width, bitmap.rows };
+ RECT targetrect;
+ targetrect.top = m_posY + ch->offsetY;
+ targetrect.left = m_posX + bitGlyph->left;
+ targetrect.bottom = targetrect.top + bitmap.rows;
+ targetrect.right = targetrect.left + bitmap.width;
+
+ D3DXLoadSurfaceFromMemory( target, NULL, &targetrect,
+ bitmap.buffer, D3DFMT_LIN_A8, bitmap.pitch, NULL, &sourcerect,
+ D3DX_FILTER_NONE, 0x00000000);
+
+ SAFE_RELEASE(target);
+
+ return TRUE;
+}
+
+
+void CGUIFontTTFDX::DeleteHardwareTexture()
+{
+
+}
+
+
+#endif
diff --git a/guilib/GUIFontTTFDX.h b/guilib/GUIFontTTFDX.h new file mode 100644 index 0000000000..96b433d77a --- /dev/null +++ b/guilib/GUIFontTTFDX.h @@ -0,0 +1,55 @@ +/*
+* Copyright (C) 2005-2008 Team XBMC
+* http://www.xbmc.org
+*
+* 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, 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 XBMC; see the file COPYING. If not, write to
+* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+* http://www.gnu.org/copyleft/gpl.html
+*
+*/
+
+/*!
+\file GUIFont.h
+\brief
+*/
+
+#ifndef CGUILIB_GUIFONTTTF_DX_H
+#define CGUILIB_GUIFONTTTF_DX_H
+#pragma once
+
+
+#include "GUIFontTTF.h"
+
+
+/*!
+ \ingroup textures
+ \brief
+ */
+class CGUIFontTTFDX : public CGUIFontTTFBase
+{
+public:
+ CGUIFontTTFDX(const CStdString& strFileName);
+ virtual ~CGUIFontTTFDX(void);
+
+ virtual void Begin();
+ virtual void End();
+
+protected:
+ virtual CBaseTexture* ReallocTexture(unsigned int& newHeight);
+ virtual bool CopyCharToTexture(FT_BitmapGlyph bitGlyph, Character *ch);
+ virtual void DeleteHardwareTexture();
+ virtual void RenderInternal(SVertex* v);
+};
+
+#endif
diff --git a/guilib/GUIFontTTFGL.cpp b/guilib/GUIFontTTFGL.cpp new file mode 100644 index 0000000000..7472bafba1 --- /dev/null +++ b/guilib/GUIFontTTFGL.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" +#include "GUIFont.h" +#include "GUIFontTTFGL.h" +#include "GUIFontManager.h" +#include "Texture.h" +#include "GraphicContext.h" +#include "gui3d.h" +#include "utils/log.h" + +// stuff for freetype +#ifndef _LINUX +#include "ft2build.h" +#else +#include <ft2build.h> +#endif +#include FT_FREETYPE_H +#include FT_GLYPH_H +#include FT_OUTLINE_H + +using namespace std; + +#ifdef HAS_GL + + +CGUIFontTTFGL::CGUIFontTTFGL(const CStdString& strFileName) +: CGUIFontTTFBase(strFileName) +{ + +} + +CGUIFontTTFGL::~CGUIFontTTFGL(void) +{ + +} + +void CGUIFontTTFGL::Begin() +{ + if (m_nestedBeginCount == 0) + { + if (!m_bTextureLoaded) + { + // Have OpenGL generate a texture object handle for us + glGenTextures(1, (GLuint*) &m_nTexture); + + // Bind the texture object + glBindTexture(GL_TEXTURE_2D, m_nTexture); + glEnable(GL_TEXTURE_2D); + + // Set the texture's stretching properties + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Set the texture image -- THIS WORKS, so the pixels must be wrong. + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, m_texture->GetWidth(), m_texture->GetHeight(), 0, + GL_ALPHA, GL_UNSIGNED_BYTE, m_texture->GetPixels()); + + VerifyGLState(); + m_bTextureLoaded = true; + } + + // Turn Blending On + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, m_nTexture); + glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE0); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + VerifyGLState(); + + m_vertex_count = 0; + } + // Keep track of the nested begin/end calls. + m_nestedBeginCount++; +} + +void CGUIFontTTFGL::End() +{ + if (m_nestedBeginCount == 0) + return; + + if (--m_nestedBeginCount > 0) + return; + + glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); + + glColorPointer (4, GL_UNSIGNED_BYTE, sizeof(SVertex), (char*)m_vertex + offsetof(SVertex, r)); + glVertexPointer (3, GL_FLOAT , sizeof(SVertex), (char*)m_vertex + offsetof(SVertex, x)); + glTexCoordPointer(2, GL_FLOAT , sizeof(SVertex), (char*)m_vertex + offsetof(SVertex, u)); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glDrawArrays(GL_QUADS, 0, m_vertex_count); + glPopClientAttrib(); +} + +CBaseTexture* CGUIFontTTFGL::ReallocTexture(unsigned int& newHeight) +{ + newHeight = CBaseTexture::PadPow2(newHeight); + + CBaseTexture* newTexture = new CTexture(m_textureWidth, newHeight, 8); +/* yuvalt + +#ifdef __APPLE__ + // Because of an SDL bug (?), bpp gets set to 4 even though we asked for 1, in fullscreen mode. + // To be completely honest, we probably shouldn't even be using an SDL surface in OpenGL mode, since + // we only use it to store the image before copying it (no blitting!) to an OpenGL texture. + // + if (newTexture->GetPitch() != m_textureWidth) + newTexture->SetPitch(m_textureWidth); +#endif + +*/ + + if (!newTexture || newTexture->GetPixels() == NULL) + { + CLog::Log(LOGERROR, "GUIFontTTFGL::CacheCharacter: Error creating new cache texture for size %f", m_height); + return NULL; + } + m_textureHeight = newTexture->GetHeight(); + m_textureWidth = newTexture->GetWidth(); + + if (m_texture) + { + unsigned char* src = (unsigned char*) m_texture->GetPixels(); + unsigned char* dst = (unsigned char*) newTexture->GetPixels(); + for (unsigned int y = 0; y < m_texture->GetHeight(); y++) + { + memcpy(dst, src, m_texture->GetPitch()); + src += m_texture->GetPitch(); + dst += newTexture->GetPitch(); + } + delete m_texture; + } + + return newTexture; +} + +bool CGUIFontTTFGL::CopyCharToTexture(FT_BitmapGlyph bitGlyph, Character* ch) +{ + //SDL_LockSurface(m_texture); + + FT_Bitmap bitmap = bitGlyph->bitmap; + + unsigned char* source = (unsigned char*) bitmap.buffer; + unsigned char* target = (unsigned char*) m_texture->GetPixels() + (m_posY + ch->offsetY) * m_texture->GetPitch() + m_posX + bitGlyph->left; + + for (int y = 0; y < bitmap.rows; y++) + { + memcpy(target, source, bitmap.width); + source += bitmap.width; + target += m_texture->GetPitch(); + } + // THE SOURCE VALUES ARE THE SAME IN BOTH SITUATIONS. + + // Since we have a new texture, we need to delete the old one + // the Begin(); End(); stuff is handled by whoever called us + if (m_bTextureLoaded) + { + g_graphicsContext.BeginPaint(); //FIXME + DeleteHardwareTexture(); + g_graphicsContext.EndPaint(); + m_bTextureLoaded = false; + } + + //SDL_UnlockSurface(m_texture); + + return TRUE; +} + + +void CGUIFontTTFGL::DeleteHardwareTexture() +{ + if (m_bTextureLoaded) + { + if (glIsTexture(m_nTexture)) + glDeleteTextures(1, (GLuint*) &m_nTexture); + m_bTextureLoaded = false; + } +} + +#endif diff --git a/guilib/GUIFontTTFGL.h b/guilib/GUIFontTTFGL.h new file mode 100644 index 0000000000..b23a9d3af3 --- /dev/null +++ b/guilib/GUIFontTTFGL.h @@ -0,0 +1,56 @@ +/* +* Copyright (C) 2005-2008 Team XBMC +* http://www.xbmc.org +* +* 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, 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 XBMC; see the file COPYING. If not, write to +* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +* http://www.gnu.org/copyleft/gpl.html +* +*/ + +/*! +\file GUIFont.h +\brief +*/ + +#ifndef CGUILIB_GUIFONTTTF_GL_H +#define CGUILIB_GUIFONTTTF_GL_H +#pragma once + + +#include "GUIFontTTF.h" + + +/*! + \ingroup textures + \brief + */ +class CGUIFontTTFGL : public CGUIFontTTFBase +{ +public: + CGUIFontTTFGL(const CStdString& strFileName); + virtual ~CGUIFontTTFGL(void); + + virtual void Begin(); + virtual void End(); + +protected: + virtual CBaseTexture* ReallocTexture(unsigned int& newHeight); + virtual bool CopyCharToTexture(FT_BitmapGlyph bitGlyph, Character *ch); + virtual void DeleteHardwareTexture(); + virtual void RenderInternal(SVertex* v) {} + +}; + +#endif diff --git a/guilib/GUIFontTTFGLES.cpp b/guilib/GUIFontTTFGLES.cpp new file mode 100644 index 0000000000..8adcefebc2 --- /dev/null +++ b/guilib/GUIFontTTFGLES.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" +#include "GUIFont.h" +#include "GUIFontTTFGL.h" +#include "GUIFontManager.h" +#include "Texture.h" +#include "GraphicContext.h" +#include "gui3d.h" + +// stuff for freetype +#ifndef _LINUX +#include "ft2build.h" +#else +#include <ft2build.h> +#endif +#include FT_FREETYPE_H +#include FT_GLYPH_H +#include FT_OUTLINE_H + +using namespace std; + +#ifdef HAS_GLES + +CGUIFontTTFGLES::CGUIFontTTFGLES(const CStdString& strFileName) +: CGUIFontTTFBase(strFileName) +{ + +} + +CGUIFontTTFGLES::~CGUIFontTTFGLES(void) +{ + +} + +void CGUIFontTTFGLES::Begin() +{ + // TODO: GLES +} + +void CGUIFontTTFGLES::End() +{ + // TODO: GLES +} + +CBaseTexture* CGUIFontTTFGLES::ReallocTexture(unsigned int& newHeight) +{ + // TODO: GLES + return NULL; +} + +bool CGUIFontTTFGLES::CopyCharToTexture(FT_BitmapGlyph bitGlyph, Character* ch) +{ + // TODO: GLES + return false; +} + +void CGUIFontTTFGLES::DeleteHardwareTexture() +{ + // TODO: GLES +} + +#endif diff --git a/guilib/GUIFontTTFGLES.h b/guilib/GUIFontTTFGLES.h new file mode 100644 index 0000000000..207da8c57f --- /dev/null +++ b/guilib/GUIFontTTFGLES.h @@ -0,0 +1,56 @@ +/* +* Copyright (C) 2005-2008 Team XBMC +* http://www.xbmc.org +* +* 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, 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 XBMC; see the file COPYING. If not, write to +* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +* http://www.gnu.org/copyleft/gpl.html +* +*/ + +/*! +\file GUIFont.h +\brief +*/ + +#ifndef CGUILIB_GUIFONTTTF_GLES_H +#define CGUILIB_GUIFONTTTF_GLES_H +#pragma once + + +#include "GUIFontTTF.h" + + +/*! + \ingroup textures + \brief + */ +class CGUIFontTTFGLES : public CGUIFontTTFBase +{ +public: + CGUIFontTTFGLES(const CStdString& strFileName); + virtual ~CGUIFontTTFGLES(void); + + virtual void Begin(); + virtual void End(); + +protected: + virtual CBaseTexture* ReallocTexture(unsigned int& newHeight); + virtual bool CopyCharToTexture(FT_BitmapGlyph bitGlyph, Character *ch); + virtual void DeleteHardwareTexture(); + virtual void RenderInternal(SVertex* v) {} + +}; + +#endif diff --git a/guilib/GUIImage.cpp b/guilib/GUIImage.cpp new file mode 100644 index 0000000000..b2f444eea4 --- /dev/null +++ b/guilib/GUIImage.cpp @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIImage.h" +#include "TextureManager.h" +#include "utils/log.h" + +using namespace std; + +CGUIImage::CGUIImage(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& texture) + : CGUIControl(parentID, controlID, posX, posY, width, height) + , m_texture(posX, posY, width, height, texture) +{ + m_crossFadeTime = 0; + m_currentFadeTime = 0; + m_lastRenderTime = 0; + ControlType = GUICONTROL_IMAGE; + m_bDynamicResourceAlloc=false; +} + +CGUIImage::CGUIImage(const CGUIImage &left) + : CGUIControl(left), m_texture(left.m_texture) +{ + m_info = left.m_info; + m_crossFadeTime = left.m_crossFadeTime; + // defaults + m_currentFadeTime = 0; + m_lastRenderTime = 0; + ControlType = GUICONTROL_IMAGE; + m_bDynamicResourceAlloc=false; +} + +CGUIImage::~CGUIImage(void) +{ + +} + +void CGUIImage::UpdateVisibility(const CGUIListItem *item) +{ + CGUIControl::UpdateVisibility(item); + + // now that we've checked for conditional info, we can + // check for allocation + AllocateOnDemand(); +} + +void CGUIImage::UpdateInfo(const CGUIListItem *item) +{ + if (m_info.IsConstant()) + return; // nothing to do + + // don't allow image to change while animating out + if (HasRendered() && IsAnimating(ANIM_TYPE_HIDDEN) && !IsVisibleFromSkin()) + return; + + if (item) + SetFileName(m_info.GetItemLabel(item, true)); + else + SetFileName(m_info.GetLabel(m_parentID, true)); +} + +void CGUIImage::AllocateOnDemand() +{ + // if we're hidden, we can free our resources and return + if (!IsVisible() && m_visible != DELAYED) + { + if (m_bDynamicResourceAlloc && m_texture.IsAllocated()) + FreeResourcesButNotAnims(); + return; + } + + // either visible or delayed - we need the resources allocated in either case + if (!m_texture.IsAllocated()) + AllocResources(); +} + +void CGUIImage::Render() +{ + if (!IsVisible()) return; + + // check whether our image failed to allocate, and if so drop back to the fallback image + if (m_texture.FailedToAlloc() && !m_texture.GetFileName().Equals(m_info.GetFallback())) + m_texture.SetFileName(m_info.GetFallback()); + + if (m_crossFadeTime) + { + // make sure our texture has started allocating + m_texture.AllocResources(); + + // compute the frame time + unsigned int frameTime = 0; + unsigned int currentTime = timeGetTime(); + if (m_lastRenderTime) + frameTime = currentTime - m_lastRenderTime; + m_lastRenderTime = currentTime; + + if (m_fadingTextures.size()) // have some fading images + { // anything other than the last old texture needs to be faded out as per usual + for (vector<CFadingTexture *>::iterator i = m_fadingTextures.begin(); i != m_fadingTextures.end() - 1;) + { + if (!RenderFading(*i, frameTime)) + i = m_fadingTextures.erase(i); + else + i++; + } + + if (m_texture.ReadyToRender() || m_texture.GetFileName().IsEmpty()) + { // fade out the last one as well + if (!RenderFading(m_fadingTextures[m_fadingTextures.size() - 1], frameTime)) + m_fadingTextures.erase(m_fadingTextures.end() - 1); + } + else + { // keep the last one fading in + CFadingTexture *texture = m_fadingTextures[m_fadingTextures.size() - 1]; + texture->m_fadeTime += frameTime; + if (texture->m_fadeTime > m_crossFadeTime) + texture->m_fadeTime = m_crossFadeTime; + texture->m_texture->SetAlpha(GetFadeLevel(texture->m_fadeTime)); + texture->m_texture->SetDiffuseColor(m_diffuseColor); + texture->m_texture->Render(); + } + } + + if (m_texture.ReadyToRender() || m_texture.GetFileName().IsEmpty()) + { // fade the new one in + m_currentFadeTime += frameTime; + if (m_currentFadeTime > m_crossFadeTime || frameTime == 0) // for if we allocate straight away on creation + m_currentFadeTime = m_crossFadeTime; + } + m_texture.SetAlpha(GetFadeLevel(m_currentFadeTime)); + } + + m_texture.SetDiffuseColor(m_diffuseColor); + m_texture.Render(); + + CGUIControl::Render(); +} + +bool CGUIImage::RenderFading(CGUIImage::CFadingTexture *texture, unsigned int frameTime) +{ + assert(texture); + if (texture->m_fadeTime <= frameTime) + { // time to kill off the texture + delete texture; + return false; + } + // render this texture + texture->m_fadeTime -= frameTime; + texture->m_texture->SetAlpha(GetFadeLevel(texture->m_fadeTime)); + texture->m_texture->SetDiffuseColor(m_diffuseColor); + texture->m_texture->Render(); + return true; +} + +bool CGUIImage::OnAction(const CAction &action) +{ + return false; +} + +bool CGUIImage::OnMessage(CGUIMessage& message) +{ + if (message.GetMessage() == GUI_MSG_REFRESH_THUMBS) + { + if (!m_info.IsConstant()) + FreeTextures(true); // true as we want to free the texture immediately + return true; + } + return CGUIControl::OnMessage(message); +} + +void CGUIImage::AllocResources() +{ + if (m_texture.GetFileName().IsEmpty()) + return; + + CGUIControl::AllocResources(); + m_texture.AllocResources(); +} + +void CGUIImage::FreeTextures(bool immediately /* = false */) +{ + m_texture.FreeResources(immediately); + for (unsigned int i = 0; i < m_fadingTextures.size(); i++) + delete m_fadingTextures[i]; + m_fadingTextures.clear(); +} + +void CGUIImage::FreeResources() +{ + FreeTextures(); + CGUIControl::FreeResources(); +} + +// WORKAROUND - we are currently resetting all animations when this is called, which shouldn't be the case +// see CGUIControl::FreeResources() - this needs remedying. +void CGUIImage::FreeResourcesButNotAnims() +{ + FreeTextures(); + m_bAllocated=false; + m_hasRendered = false; +} + +void CGUIImage::DynamicResourceAlloc(bool bOnOff) +{ + m_bDynamicResourceAlloc = bOnOff; + m_texture.DynamicResourceAlloc(bOnOff); + CGUIControl::DynamicResourceAlloc(bOnOff); +} + +bool CGUIImage::CanFocus() const +{ + return false; +} + +float CGUIImage::GetTextureWidth() const +{ + return m_texture.GetTextureWidth(); +} + +float CGUIImage::GetTextureHeight() const +{ + return m_texture.GetTextureHeight(); +} + +const CStdString &CGUIImage::GetFileName() const +{ + return m_texture.GetFileName(); +} + +void CGUIImage::SetAspectRatio(const CAspectRatio &aspect) +{ + m_texture.SetAspectRatio(aspect); +} + +void CGUIImage::SetCrossFade(unsigned int time) +{ + m_crossFadeTime = time; + if (!m_crossFadeTime && m_texture.IsLazyLoaded() && !m_info.GetFallback().IsEmpty()) + m_crossFadeTime = 1; +} + +void CGUIImage::SetFileName(const CStdString& strFileName, bool setConstant) +{ + if (setConstant) + m_info.SetLabel(strFileName, ""); + + if (m_crossFadeTime) + { + // set filename on the next texture + if (m_currentTexture.Equals(strFileName)) + return; // nothing to do - we already have this image + + if (m_texture.ReadyToRender() || m_texture.GetFileName().IsEmpty()) + { // save the current image + m_fadingTextures.push_back(new CFadingTexture(m_texture, m_currentFadeTime)); + } + m_currentFadeTime = 0; + } + if (!m_currentTexture.Equals(strFileName)) + { // texture is changing - attempt to load it, and save the name in m_currentTexture. + // we'll check whether it loaded or not in Render() + m_currentTexture = strFileName; + m_texture.SetFileName(m_currentTexture); + } +} + +#ifdef _DEBUG +void CGUIImage::DumpTextureUse() +{ + if (m_texture.IsAllocated()) + { + if (GetID()) + CLog::Log(LOGDEBUG, "Image control %u using texture %s", + GetID(), m_texture.GetFileName().c_str()); + else + CLog::Log(LOGDEBUG, "Using texture %s", m_texture.GetFileName().c_str()); + } +} +#endif + +void CGUIImage::SetWidth(float width) +{ + m_texture.SetWidth(width); + CGUIControl::SetWidth(m_texture.GetWidth()); +} + +void CGUIImage::SetHeight(float height) +{ + m_texture.SetHeight(height); + CGUIControl::SetHeight(m_texture.GetHeight()); +} + +void CGUIImage::SetPosition(float posX, float posY) +{ + m_texture.SetPosition(posX, posY); + CGUIControl::SetPosition(posX, posY); +} + +void CGUIImage::SetInfo(const CGUIInfoLabel &info) +{ + m_info = info; + // a constant image never needs updating + if (m_info.IsConstant()) + m_texture.SetFileName(m_info.GetLabel(0)); +} + +unsigned char CGUIImage::GetFadeLevel(unsigned int time) const +{ + float amount = (float)time / m_crossFadeTime; + // we want a semi-transparent image, so we need to use a more complicated + // fade technique. Assuming a black background (not generally true, but still...) + // we have + // b(t) = [a - b(1-t)*a] / a*(1-b(1-t)*a), + // where a = alpha, and b(t):[0,1] -> [0,1] is the blend function. + // solving, we get + // b(t) = [1 - (1-a)^t] / a + const float alpha = 0.7f; + return (unsigned char)(255.0f * (1 - pow(1-alpha, amount))/alpha); +} + +CStdString CGUIImage::GetDescription(void) const +{ + return GetFileName(); +} + diff --git a/guilib/GUIImage.h b/guilib/GUIImage.h new file mode 100644 index 0000000000..cc9651c9e2 --- /dev/null +++ b/guilib/GUIImage.h @@ -0,0 +1,119 @@ +/*! +\file guiImage.h +\brief +*/ + +#ifndef GUILIB_GUIIMAGECONTROL_H +#define GUILIB_GUIIMAGECONTROL_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControl.h" +#include "GUITexture.h" +#include "GUILabelControl.h" // for CGUIInfoLabel + +/*! + \ingroup controls + \brief + */ + +class CGUIImage : public CGUIControl +{ +public: + class CFadingTexture + { + public: + CFadingTexture(const CGUITexture &texture, unsigned int fadeTime) + { + // create a copy of our texture, and allocate resources + m_texture = new CGUITexture(texture); + m_texture->AllocResources(); + m_fadeTime = fadeTime; + m_fading = false; + }; + ~CFadingTexture() + { + m_texture->FreeResources(); + delete m_texture; + }; + + CGUITexture *m_texture; ///< texture to fade out + unsigned int m_fadeTime; ///< time to fade out (ms) + bool m_fading; ///< whether we're fading out + }; + + CGUIImage(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& texture); + CGUIImage(const CGUIImage &left); + virtual ~CGUIImage(void); + virtual CGUIImage *Clone() const { return new CGUIImage(*this); }; + + virtual void Render(); + virtual void UpdateVisibility(const CGUIListItem *item = NULL); + virtual bool OnAction(const CAction &action) ; + virtual bool OnMessage(CGUIMessage& message); + virtual void AllocResources(); + virtual void FreeResources(); + virtual void DynamicResourceAlloc(bool bOnOff); + virtual bool IsDynamicallyAllocated() { return m_bDynamicResourceAlloc; }; + virtual bool CanFocus() const; + virtual void UpdateInfo(const CGUIListItem *item = NULL); + + virtual void SetInfo(const CGUIInfoLabel &info); + virtual void SetFileName(const CStdString& strFileName, bool setConstant = false); + virtual void SetAspectRatio(const CAspectRatio &aspect); + virtual void SetWidth(float width); + virtual void SetHeight(float height); + virtual void SetPosition(float posX, float posY); + virtual CStdString GetDescription() const; + void SetCrossFade(unsigned int time); + + const CStdString& GetFileName() const; + float GetTextureWidth() const; + float GetTextureHeight() const; + +#ifdef _DEBUG + virtual void DumpTextureUse(); +#endif +protected: + virtual void AllocateOnDemand(); + virtual void FreeTextures(bool immediately = false); + void FreeResourcesButNotAnims(); + unsigned char GetFadeLevel(unsigned int time) const; + bool RenderFading(CFadingTexture *texture, unsigned int frameTime); + + bool m_bDynamicResourceAlloc; + + // border + conditional info + CTextureInfo m_image; + CGUIInfoLabel m_info; + + CGUITexture m_texture; + std::vector<CFadingTexture *> m_fadingTextures; + CStdString m_currentTexture; + + unsigned int m_crossFadeTime; + unsigned int m_currentFadeTime; + unsigned int m_lastRenderTime; +}; +#endif diff --git a/guilib/GUIIncludes.cpp b/guilib/GUIIncludes.cpp new file mode 100644 index 0000000000..e88f3ad6b0 --- /dev/null +++ b/guilib/GUIIncludes.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIIncludes.h" +#include "SkinInfo.h" +#include "utils/GUIInfoManager.h" +#include "utils/log.h" +#include "tinyXML/tinyxml.h" + +using namespace std; + +CGUIIncludes::CGUIIncludes() +{ +} + +CGUIIncludes::~CGUIIncludes() +{ +} + +void CGUIIncludes::ClearIncludes() +{ + m_includes.clear(); + m_defaults.clear(); + m_constants.clear(); + m_files.clear(); +} + +bool CGUIIncludes::LoadIncludes(const CStdString &includeFile) +{ + // check to see if we already have this loaded + if (HasIncludeFile(includeFile)) + return true; + + TiXmlDocument doc; + if (!doc.LoadFile(includeFile)) + { + CLog::Log(LOGINFO, "Error loading includes.xml file (%s): %s (row=%i, col=%i)", includeFile.c_str(), doc.ErrorDesc(), doc.ErrorRow(), doc.ErrorCol()); + return false; + } + // success, load the tags + if (LoadIncludesFromXML(doc.RootElement())) + { + m_files.push_back(includeFile); + return true; + } + return false; +} + +bool CGUIIncludes::LoadIncludesFromXML(const TiXmlElement *root) +{ + if (!root || strcmpi(root->Value(), "includes")) + { + CLog::Log(LOGERROR, "Skin includes must start with the <includes> tag"); + return false; + } + const TiXmlElement* node = root->FirstChildElement("include"); + while (node) + { + if (node->Attribute("name") && node->FirstChild()) + { + CStdString tagName = node->Attribute("name"); + m_includes.insert(pair<CStdString, TiXmlElement>(tagName, *node)); + } + else if (node->Attribute("file")) + { // load this file in as well + RESOLUTION res; + LoadIncludes(g_SkinInfo.GetSkinPath(node->Attribute("file"), &res)); + } + node = node->NextSiblingElement("include"); + } + // now defaults + node = root->FirstChildElement("default"); + while (node) + { + if (node->Attribute("type") && node->FirstChild()) + { + CStdString tagName = node->Attribute("type"); + m_defaults.insert(pair<CStdString, TiXmlElement>(tagName, *node)); + } + node = node->NextSiblingElement("default"); + } + // and finally constants + node = root->FirstChildElement("constant"); + while (node) + { + if (node->Attribute("name") && node->FirstChild()) + { + CStdString tagName = node->Attribute("name"); + m_constants.insert(pair<CStdString, float>(tagName, (float)atof(node->FirstChild()->Value()))); + } + node = node->NextSiblingElement("constant"); + } + return true; +} + +bool CGUIIncludes::HasIncludeFile(const CStdString &file) const +{ + for (iFiles it = m_files.begin(); it != m_files.end(); ++it) + if (*it == file) return true; + return false; +} + +void CGUIIncludes::ResolveIncludes(TiXmlElement *node, const CStdString &type) +{ + // we have a node, find any <include file="fileName">tagName</include> tags and replace + // recursively with their real includes + if (!node) return; + + // First add the defaults if this is for a control + if (!type.IsEmpty()) + { // resolve defaults + map<CStdString, TiXmlElement>::iterator it = m_defaults.find(type); + if (it != m_defaults.end()) + { + const TiXmlElement &element = (*it).second; + const TiXmlElement *tag = element.FirstChildElement(); + while (tag) + { + // we insert at the end of block + node->InsertEndChild(*tag); + tag = tag->NextSiblingElement(); + } + } + } + TiXmlElement *include = node->FirstChildElement("include"); + while (include && include->FirstChild()) + { + // have an include tag - grab it's tag name and replace it with the real tag contents + const char *file = include->Attribute("file"); + if (file) + { // we need to load this include from the alternative file + RESOLUTION res; + LoadIncludes(g_SkinInfo.GetSkinPath(file, &res)); + } + const char *condition = include->Attribute("condition"); + if (condition) + { // check this condition + if (!g_infoManager.GetBool(g_infoManager.TranslateString(condition))) + { + include = include->NextSiblingElement("include"); + continue; + } + } + CStdString tagName = include->FirstChild()->Value(); + map<CStdString, TiXmlElement>::iterator it = m_includes.find(tagName); + if (it != m_includes.end()) + { // found the tag(s) to include - let's replace it + const TiXmlElement &element = (*it).second; + const TiXmlElement *tag = element.FirstChildElement(); + while (tag) + { + // we insert before the <include> element to keep the correct + // order (we render in the order given in the xml file) + node->InsertBeforeChild(include, *tag); + tag = tag->NextSiblingElement(); + } + // remove the <include>tagName</include> element + node->RemoveChild(include); + include = node->FirstChildElement("include"); + } + else + { // invalid include + CLog::Log(LOGWARNING, "Skin has invalid include: %s", tagName.c_str()); + include = include->NextSiblingElement("include"); + } + } +} + +bool CGUIIncludes::ResolveConstant(const CStdString &constant, float &value) +{ + map<CStdString, float>::iterator it = m_constants.find(constant); + if (it == m_constants.end()) + value = (float)atof(constant.c_str()); + else + value = it->second; + return true; +} + diff --git a/guilib/GUIIncludes.h b/guilib/GUIIncludes.h new file mode 100644 index 0000000000..5cf68eb631 --- /dev/null +++ b/guilib/GUIIncludes.h @@ -0,0 +1,51 @@ +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "StdString.h" + +#include <map> + +// forward definitions +class TiXmlElement; + +class CGUIIncludes +{ +public: + CGUIIncludes(); + ~CGUIIncludes(); + + void ClearIncludes(); + bool LoadIncludes(const CStdString &includeFile); + void ResolveIncludes(TiXmlElement *node, const CStdString &type); + bool ResolveConstant(const CStdString &constant, float &value); + bool LoadIncludesFromXML(const TiXmlElement *root); + +private: + bool HasIncludeFile(const CStdString &includeFile) const; + std::map<CStdString, TiXmlElement> m_includes; + std::map<CStdString, TiXmlElement> m_defaults; + std::map<CStdString, float> m_constants; + std::vector<CStdString> m_files; + typedef std::vector<CStdString>::const_iterator iFiles; +}; + diff --git a/guilib/GUIInfoColor.cpp b/guilib/GUIInfoColor.cpp new file mode 100644 index 0000000000..42450f54dd --- /dev/null +++ b/guilib/GUIInfoColor.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIInfoColor.h" +#include "utils/CharsetConverter.h" +#include "utils/GUIInfoManager.h" +#include "LocalizeStrings.h" +#include "GUIColorManager.h" + +using namespace std; + +CGUIInfoBool::CGUIInfoBool(bool value) +{ + m_info = 0; + m_value = value; +} + +void CGUIInfoBool::Parse(const CStdString &info) +{ + m_info = g_infoManager.TranslateString(info); + if (m_info == SYSTEM_ALWAYS_TRUE) + { + m_value = true; + m_info = 0; + } + else if (m_info == SYSTEM_ALWAYS_FALSE) + { + m_value = false; + m_info = 0; + } + else + m_info = g_infoManager.GetBool(m_info); +} + +void CGUIInfoBool::Update(int parentID, const CGUIListItem *item) +{ + if (m_info) + m_value = g_infoManager.GetBool(m_info, parentID, item); +} + + +CGUIInfoColor::CGUIInfoColor(uint32_t color) +{ + m_color = color; + m_info = 0; +} + +const CGUIInfoColor &CGUIInfoColor::operator=(color_t color) +{ + m_color = color; + m_info = 0; + return *this; +} + +const CGUIInfoColor &CGUIInfoColor::operator=(const CGUIInfoColor &color) +{ + m_color = color.m_color; + m_info = color.m_info; + return *this; +} + +void CGUIInfoColor::Update() +{ + if (!m_info) + return; // no infolabel + + // Expand the infolabel, and then convert it to a color + CStdString infoLabel(g_infoManager.GetLabel(m_info)); + if (!infoLabel.IsEmpty()) + m_color = g_colorManager.GetColor(infoLabel.c_str()); + else + m_color = 0; +} + +void CGUIInfoColor::Parse(const CStdString &label) +{ + // Check for the standard $INFO[] block layout, and strip it if present + CStdString label2 = label; + if (label.Equals("-", false)) + return; + + if (label.Left(5).Equals("$INFO", false)) + label2 = label.Mid(6, label.length()-7); + + m_info = g_infoManager.TranslateString(label2); + if (!m_info) + m_color = g_colorManager.GetColor(label); +} + diff --git a/guilib/GUIInfoColor.h b/guilib/GUIInfoColor.h new file mode 100644 index 0000000000..612eefef95 --- /dev/null +++ b/guilib/GUIInfoColor.h @@ -0,0 +1,69 @@ +/*! +\file GUIInfoColor.h +\brief +*/ + +#ifndef GUILIB_GUIINFOCOLOR_H +#define GUILIB_GUIINFOCOLOR_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "StdString.h" + +class CGUIListItem; + +class CGUIInfoBool +{ +public: + CGUIInfoBool(bool value = false); + operator bool() const { return m_value; }; + + void Update(int parentID = 0, const CGUIListItem *item = NULL); + void Parse(const CStdString &info); +private: + int m_info; + bool m_value; +}; + +typedef uint32_t color_t; + +class CGUIInfoColor +{ +public: + CGUIInfoColor(color_t color = 0); + + const CGUIInfoColor &operator=(const CGUIInfoColor &color); + const CGUIInfoColor &operator=(color_t color); + operator color_t() const { return m_color; }; + + void Update(); + void Parse(const CStdString &label); + +private: + color_t GetColor() const; + int m_info; + color_t m_color; +}; + +#endif diff --git a/guilib/GUILabelControl.cpp b/guilib/GUILabelControl.cpp new file mode 100644 index 0000000000..6b4a86f621 --- /dev/null +++ b/guilib/GUILabelControl.cpp @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUILabelControl.h" +#include "utils/CharsetConverter.h" +#include "utils/GUIInfoManager.h" +#include "utils/log.h" +#include "GUIListItem.h" +#include "StringUtils.h" + +#include "LocalizeStrings.h" // for CGUIInfoLabel + +using namespace std; + +CGUIInfoLabel::CGUIInfoLabel() +{ +} + +CGUIInfoLabel::CGUIInfoLabel(const CStdString &label, const CStdString &fallback) +{ + SetLabel(label, fallback); +} + +void CGUIInfoLabel::SetLabel(const CStdString &label, const CStdString &fallback) +{ + m_fallback = fallback; + Parse(label); +} + +CStdString CGUIInfoLabel::GetLabel(int contextWindow, bool preferImage) const +{ + CStdString label; + for (unsigned int i = 0; i < m_info.size(); i++) + { + const CInfoPortion &portion = m_info[i]; + if (portion.m_info) + { + CStdString infoLabel; + if (preferImage) + infoLabel = g_infoManager.GetImage(portion.m_info, contextWindow); + if (infoLabel.IsEmpty()) + infoLabel = g_infoManager.GetLabel(portion.m_info, contextWindow); + if (!infoLabel.IsEmpty()) + { + label += portion.m_prefix; + label += infoLabel; + label += portion.m_postfix; + } + } + else + { // no info, so just append the prefix + label += portion.m_prefix; + } + } + if (label.IsEmpty()) // empty label, use the fallback + return m_fallback; + return label; +} + +CStdString CGUIInfoLabel::GetItemLabel(const CGUIListItem *item, bool preferImages) const +{ + if (!item->IsFileItem()) return ""; + CStdString label; + for (unsigned int i = 0; i < m_info.size(); i++) + { + const CInfoPortion &portion = m_info[i]; + if (portion.m_info) + { + CStdString infoLabel; + if (preferImages) + infoLabel = g_infoManager.GetItemImage((const CFileItem *)item, portion.m_info); + else + infoLabel = g_infoManager.GetItemLabel((const CFileItem *)item, portion.m_info); + if (!infoLabel.IsEmpty()) + { + label += portion.m_prefix; + label += infoLabel; + label += portion.m_postfix; + } + } + else + { // no info, so just append the prefix + label += portion.m_prefix; + } + } + if (label.IsEmpty()) + return m_fallback; + return label; +} + +bool CGUIInfoLabel::IsEmpty() const +{ + return m_info.size() == 0; +} + +bool CGUIInfoLabel::IsConstant() const +{ + return m_info.size() == 0 || (m_info.size() == 1 && m_info[0].m_info == 0); +} + +void CGUIInfoLabel::Parse(const CStdString &label) +{ + m_info.clear(); + CStdString work(label); + // Step 1: Replace all $LOCALIZE[number] with the real string + int pos1 = work.Find("$LOCALIZE["); + while (pos1 >= 0) + { + int pos2 = StringUtils::FindEndBracket(work, '[', ']', pos1 + 10); + if (pos2 > pos1) + { + CStdString left = work.Left(pos1); + CStdString right = work.Mid(pos2 + 1); + CStdString replace = g_localizeStringsTemp.Get(atoi(work.Mid(pos1 + 10).c_str())); + if (replace == "") + replace = g_localizeStrings.Get(atoi(work.Mid(pos1 + 10).c_str())); + work = left + replace + right; + } + else + { + CLog::Log(LOGERROR, "Error parsing label - missing ']'"); + return; + } + pos1 = work.Find("$LOCALIZE[", pos1); + } + // Step 2: Find all $INFO[info,prefix,postfix] blocks + pos1 = work.Find("$INFO["); + while (pos1 >= 0) + { + // output the first block (contents before first $INFO) + if (pos1 > 0) + m_info.push_back(CInfoPortion(0, work.Left(pos1), "")); + + // ok, now decipher the $INFO block + int pos2 = StringUtils::FindEndBracket(work, '[', ']', pos1 + 6); + if (pos2 > pos1) + { + // decipher the block + CStdString block = work.Mid(pos1 + 6, pos2 - pos1 - 6); + CStdStringArray params; + StringUtils::SplitString(block, ",", params); + int info = g_infoManager.TranslateString(params[0]); + CStdString prefix, postfix; + if (params.size() > 1) + prefix = params[1]; + if (params.size() > 2) + postfix = params[2]; + m_info.push_back(CInfoPortion(info, prefix, postfix)); + // and delete it from our work string + work = work.Mid(pos2 + 1); + } + else + { + CLog::Log(LOGERROR, "Error parsing label - missing ']'"); + return; + } + pos1 = work.Find("$INFO["); + } + // add any last block + if (!work.IsEmpty()) + m_info.push_back(CInfoPortion(0, work, "")); +} + +CGUIInfoLabel::CInfoPortion::CInfoPortion(int info, const CStdString &prefix, const CStdString &postfix) +{ + m_info = info; + m_prefix = prefix; + m_postfix = postfix; + // filter our prefix and postfix for comma's + m_prefix.Replace("$COMMA", ","); + m_postfix.Replace("$COMMA", ","); + m_prefix.Replace("$LBRACKET", "["); m_prefix.Replace("$RBRACKET", "]"); + m_postfix.Replace("$LBRACKET", "["); m_postfix.Replace("$RBRACKET", "]"); +} + +CStdString CGUIInfoLabel::GetLabel(const CStdString &label, bool preferImage) +{ // translate the label + CGUIInfoLabel info(label, ""); + return info.GetLabel(0, preferImage); +} + +CGUILabelControl::CGUILabelControl(int parentID, int controlID, float posX, float posY, float width, float height, const CLabelInfo& labelInfo, bool wrapMultiLine, bool bHasPath) + : CGUIControl(parentID, controlID, posX, posY, width, height), m_textLayout(labelInfo.font, wrapMultiLine) +{ + m_bHasPath = bHasPath; + m_iCursorPos = 0; + m_label = labelInfo; + m_bShowCursor = false; + m_dwCounter = 0; + ControlType = GUICONTROL_LABEL; + m_ScrollInsteadOfTruncate = false; + m_startHighlight = m_endHighlight = 0; +} + +CGUILabelControl::~CGUILabelControl(void) +{ +} + +void CGUILabelControl::ShowCursor(bool bShow) +{ + m_bShowCursor = bShow; +} + +void CGUILabelControl::SetCursorPos(int iPos) +{ + CStdString label = m_infoLabel.GetLabel(m_parentID); + if (iPos > (int)label.length()) iPos = label.length(); + if (iPos < 0) iPos = 0; + m_iCursorPos = iPos; +} + +void CGUILabelControl::SetInfo(const CGUIInfoLabel &infoLabel) +{ + m_infoLabel = infoLabel; +} + +void CGUILabelControl::UpdateColors() +{ + m_label.UpdateColors(); + CGUIControl::UpdateColors(); +} + +void CGUILabelControl::UpdateInfo(const CGUIListItem *item) +{ + CStdString label(m_infoLabel.GetLabel(m_parentID)); + + if (m_bShowCursor) + { // cursor location assumes utf16 text, so deal with that (inefficient, but it's not as if it's a high-use area + // virtual keyboard only) + CStdStringW utf16; + g_charsetConverter.utf8ToW(label, utf16); + CStdStringW col; + if ((++m_dwCounter % 50) > 25) + col.Format(L"|"); + else + col.Format(L"[COLOR %x]|[/COLOR]", 0x1000000); + utf16.Insert(m_iCursorPos, col); + g_charsetConverter.wToUTF8(utf16, label); + } + else if (m_startHighlight || m_endHighlight) + { // this is only used for times/dates, so working in ascii (utf8) is fine + CStdString colorLabel; + colorLabel.Format("[COLOR %x]%s[/COLOR]%s[COLOR %x]%s[/COLOR]", (color_t)m_label.disabledColor, label.Left(m_startHighlight), + label.Mid(m_startHighlight, m_endHighlight - m_startHighlight), (color_t)m_label.disabledColor, label.Mid(m_endHighlight)); + label = colorLabel; + } + + if (m_textLayout.Update(label, m_width)) + { // reset the scrolling as we have a new label + m_ScrollInfo.Reset(); + } +} + +void CGUILabelControl::Render() +{ + // check for scrolling + bool bNormalDraw = true; + if (m_ScrollInsteadOfTruncate && m_width > 0 && !IsDisabled()) + { // ignore align center - just use align left/right + float width, height; + m_textLayout.GetTextExtent(width, height); + if (width > m_width) + { // need to scroll - set the viewport. Should be set just using the height of the text + bNormalDraw = false; + float fPosX = m_posX; + if (m_label.align & XBFONT_RIGHT) + fPosX -= m_width; + float fPosY = m_posY; + if (m_label.align & XBFONT_CENTER_Y) + fPosY += m_height * 0.5f; + + m_textLayout.RenderScrolling(fPosX, fPosY, m_label.angle, m_label.textColor, m_label.shadowColor, (m_label.align & ~3), m_width, m_ScrollInfo); + } + } + if (bNormalDraw) + { + float fPosX = m_posX; + if (m_label.align & XBFONT_CENTER_X) + fPosX += m_width * 0.5f; + + float fPosY = m_posY; + if (m_label.align & XBFONT_CENTER_Y) + fPosY += m_height * 0.5f; + + if (IsDisabled()) + m_textLayout.Render(fPosX, fPosY, m_label.angle, m_label.disabledColor, m_label.shadowColor, m_label.align | XBFONT_TRUNCATED, m_width, true); + else + m_textLayout.Render(fPosX, fPosY, m_label.angle, m_label.textColor, m_label.shadowColor, m_label.align | XBFONT_TRUNCATED, m_width); + } + CGUIControl::Render(); +} + + +bool CGUILabelControl::CanFocus() const +{ + return false; +} + +void CGUILabelControl::SetLabel(const string &strLabel) +{ + // shorten the path label + if ( m_bHasPath ) + m_infoLabel.SetLabel(ShortenPath(strLabel), ""); + else // parse the label for info tags + m_infoLabel.SetLabel(strLabel, ""); + m_ScrollInfo.Reset(); + if (m_iCursorPos > (int)strLabel.size()) + m_iCursorPos = strLabel.size(); +} + +void CGUILabelControl::SetWidthControl(bool bScroll, int scrollSpeed) +{ + m_ScrollInsteadOfTruncate = bScroll; + m_ScrollInfo.SetSpeed(scrollSpeed); + m_ScrollInfo.Reset(); +} + +void CGUILabelControl::SetAlignment(uint32_t align) +{ + m_label.align = align; +} + +bool CGUILabelControl::OnMessage(CGUIMessage& message) +{ + if ( message.GetControlId() == GetID() ) + { + if (message.GetMessage() == GUI_MSG_LABEL_SET) + { + SetLabel(message.GetLabel()); + return true; + } + } + + return CGUIControl::OnMessage(message); +} + +CStdString CGUILabelControl::ShortenPath(const CStdString &path) +{ + if (!m_label.font || m_width == 0 || path.IsEmpty()) + return path; + + char cDelim = '\0'; + size_t nPos; + + nPos = path.find_last_of( '\\' ); + if ( nPos != std::string::npos ) + cDelim = '\\'; + else + { + nPos = path.find_last_of( '/' ); + if ( nPos != std::string::npos ) + cDelim = '/'; + } + if ( cDelim == '\0' ) + return path; + + CStdString workPath(path); + // remove trailing slashes + if (workPath.size() > 3) + if (workPath.Right(3).Compare("://") != 0 && workPath.Right(2).Compare(":\\") != 0) + if (nPos == workPath.size() - 1) + { + workPath.erase(workPath.size() - 1); + nPos = workPath.find_last_of( cDelim ); + } + + float fTextHeight, fTextWidth; + m_textLayout.Update(workPath); + m_textLayout.GetTextExtent(fTextWidth, fTextHeight); + + while ( fTextWidth > m_width ) + { + size_t nGreaterDelim = workPath.find_last_of( cDelim, nPos ); + if (nGreaterDelim == std::string::npos) + break; + + nPos = workPath.find_last_of( cDelim, nGreaterDelim - 1 ); + if ( nPos == std::string::npos ) + break; + + workPath.replace( nPos + 1, nGreaterDelim - nPos - 1, "..." ); + + m_textLayout.Update(workPath); + m_textLayout.GetTextExtent(fTextWidth, fTextHeight); + } + return workPath; +} + +void CGUILabelControl::SetTruncate(bool bTruncate) +{ + if (bTruncate) + m_label.align |= XBFONT_TRUNCATED; + else + m_label.align &= ~XBFONT_TRUNCATED; +} + +void CGUILabelControl::SetHighlight(unsigned int start, unsigned int end) +{ + m_startHighlight = start; + m_endHighlight = end; +} + +CStdString CGUILabelControl::GetDescription() const +{ + return m_infoLabel.GetLabel(m_parentID); +} diff --git a/guilib/GUILabelControl.h b/guilib/GUILabelControl.h new file mode 100644 index 0000000000..052c635994 --- /dev/null +++ b/guilib/GUILabelControl.h @@ -0,0 +1,118 @@ +/*! +\file GUILabelControl.h +\brief +*/ + +#ifndef GUILIB_GUILABELCONTROL_H +#define GUILIB_GUILABELCONTROL_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControl.h" +#include "GUITextLayout.h" + +class CGUIListItem; + +class CGUIInfoLabel +{ +public: + CGUIInfoLabel(); + CGUIInfoLabel(const CStdString &label, const CStdString &fallback = ""); + + void SetLabel(const CStdString &label, const CStdString &fallback); + CStdString GetLabel(int contextWindow, bool preferImage = false) const; + CStdString GetItemLabel(const CGUIListItem *item, bool preferImage = false) const; + bool IsConstant() const; + bool IsEmpty() const; + + const CStdString GetFallback() const { return m_fallback; }; + + static CStdString GetLabel(const CStdString &label, bool preferImage = false); +private: + void Parse(const CStdString &label); + + class CInfoPortion + { + public: + CInfoPortion(int info, const CStdString &prefix, const CStdString &postfix); + int m_info; + CStdString m_prefix; + CStdString m_postfix; + }; + + CStdString m_fallback; + std::vector<CInfoPortion> m_info; +}; + +/*! + \ingroup controls + \brief + */ +class CGUILabelControl : + public CGUIControl +{ +public: + CGUILabelControl(int parentID, int controlID, float posX, float posY, float width, float height, const CLabelInfo& labelInfo, bool wrapMultiLine, bool bHasPath); + virtual ~CGUILabelControl(void); + virtual CGUILabelControl *Clone() const { return new CGUILabelControl(*this); }; + + virtual void Render(); + virtual void UpdateInfo(const CGUIListItem *item = NULL); + virtual bool CanFocus() const; + virtual bool OnMessage(CGUIMessage& message); + virtual CStdString GetDescription() const; + + const CLabelInfo& GetLabelInfo() const { return m_label; }; + void SetLabel(const std::string &strLabel); + void ShowCursor(bool bShow = true); + void SetCursorPos(int iPos); + int GetCursorPos() const { return m_iCursorPos;}; + void SetInfo(const CGUIInfoLabel&labelInfo); + void SetWidthControl(bool bScroll, int scrollSpeed); + void SetTruncate(bool bTruncate); + void SetAlignment(uint32_t align); + void SetHighlight(unsigned int start, unsigned int end); + +protected: + void UpdateColors(); + CStdString ShortenPath(const CStdString &path); + + CLabelInfo m_label; + CGUITextLayout m_textLayout; + + bool m_bHasPath; + bool m_bShowCursor; + int m_iCursorPos; + unsigned int m_dwCounter; + // stuff for scrolling + bool m_ScrollInsteadOfTruncate; + CScrollInfo m_ScrollInfo; + + // multi-info stuff + CGUIInfoLabel m_infoLabel; + + unsigned int m_startHighlight; + unsigned int m_endHighlight; +}; +#endif diff --git a/guilib/GUIListContainer.cpp b/guilib/GUIListContainer.cpp new file mode 100644 index 0000000000..7de8631e6c --- /dev/null +++ b/guilib/GUIListContainer.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIListContainer.h" +#include "GUIListItem.h" +#include "GUIInfoManager.h" +#include "Key.h" + +CGUIListContainer::CGUIListContainer(int parentID, int controlID, float posX, float posY, float width, float height, ORIENTATION orientation, int scrollTime, int preloadItems) + : CGUIBaseContainer(parentID, controlID, posX, posY, width, height, orientation, scrollTime, preloadItems) +{ + ControlType = GUICONTAINER_LIST; + m_type = VIEW_TYPE_LIST; +} + +CGUIListContainer::~CGUIListContainer(void) +{ +} + +bool CGUIListContainer::OnAction(const CAction &action) +{ + switch (action.id) + { + case ACTION_PAGE_UP: + { + if (m_offset == 0) + { // already on the first page, so move to the first item + SetCursor(0); + } + else + { // scroll up to the previous page + Scroll( -m_itemsPerPage); + } + return true; + } + break; + case ACTION_PAGE_DOWN: + { + if (m_offset == (int)m_items.size() - m_itemsPerPage || (int)m_items.size() < m_itemsPerPage) + { // already at the last page, so move to the last item. + SetCursor(m_items.size() - m_offset - 1); + } + else + { // scroll down to the next page + Scroll(m_itemsPerPage); + } + return true; + } + break; + // smooth scrolling (for analog controls) + case ACTION_SCROLL_UP: + { + m_analogScrollCount += action.amount1 * action.amount1; + bool handled = false; + while (m_analogScrollCount > 0.4) + { + handled = true; + m_analogScrollCount -= 0.4f; + if (m_offset > 0 && m_cursor <= m_itemsPerPage / 2) + { + Scroll(-1); + } + else if (m_cursor > 0) + { + SetCursor(m_cursor - 1); + } + } + return handled; + } + break; + case ACTION_SCROLL_DOWN: + { + m_analogScrollCount += action.amount1 * action.amount1; + bool handled = false; + while (m_analogScrollCount > 0.4) + { + handled = true; + m_analogScrollCount -= 0.4f; + if (m_offset + m_itemsPerPage < (int)m_items.size() && m_cursor >= m_itemsPerPage / 2) + { + Scroll(1); + } + else if (m_cursor < m_itemsPerPage - 1 && m_offset + m_cursor < (int)m_items.size() - 1) + { + SetCursor(m_cursor + 1); + } + } + return handled; + } + break; + } + return CGUIBaseContainer::OnAction(action); +} + +bool CGUIListContainer::OnMessage(CGUIMessage& message) +{ + if (message.GetControlId() == GetID() ) + { + if (message.GetMessage() == GUI_MSG_LABEL_RESET) + { + SetCursor(0); + } + else if (message.GetMessage() == GUI_MSG_ITEM_SELECT) + { + SelectItem(message.GetParam1()); + return true; + } + else if (message.GetMessage() == GUI_MSG_SETFOCUS) + { + if (message.GetParam1()) // subfocus item is specified, so set the offset appropriately + m_cursor = (int)message.GetParam1() - 1; + } + } + return CGUIBaseContainer::OnMessage(message); +} + +bool CGUIListContainer::MoveUp(bool wrapAround) +{ + if (m_cursor > 0) + { + SetCursor(m_cursor - 1); + } + else if (m_cursor == 0 && m_offset) + { + ScrollToOffset(m_offset - 1); + } + else if (wrapAround) + { + if (m_items.size() > 0) + { // move 2 last item in list, and set our container moving up + int offset = m_items.size() - m_itemsPerPage; + if (offset < 0) offset = 0; + SetCursor(m_items.size() - offset - 1); + ScrollToOffset(offset); + g_infoManager.SetContainerMoving(GetID(), -1); + } + } + else + return false; + return true; +} + +bool CGUIListContainer::MoveDown(bool wrapAround) +{ + if (m_offset + m_cursor + 1 < (int)m_items.size()) + { + if (m_cursor + 1 < m_itemsPerPage) + { + SetCursor(m_cursor + 1); + } + else + { + ScrollToOffset(m_offset + 1); + } + } + else if(wrapAround) + { // move first item in list, and set our container moving in the "down" direction + SetCursor(0); + ScrollToOffset(0); + g_infoManager.SetContainerMoving(GetID(), 1); + } + else + return false; + return true; +} + +// scrolls the said amount +void CGUIListContainer::Scroll(int amount) +{ + // increase or decrease the offset + int offset = m_offset + amount; + if (offset > (int)m_items.size() - m_itemsPerPage) + { + offset = m_items.size() - m_itemsPerPage; + } + if (offset < 0) offset = 0; + ScrollToOffset(offset); +} + +void CGUIListContainer::ValidateOffset() +{ // first thing is we check the range of m_offset + if (!m_layout) return; + if (m_offset > (int)m_items.size() - m_itemsPerPage) + { + m_offset = m_items.size() - m_itemsPerPage; + m_scrollOffset = m_offset * m_layout->Size(m_orientation); + } + if (m_offset < 0) + { + m_offset = 0; + m_scrollOffset = 0; + } +} + +void CGUIListContainer::SetCursor(int cursor) +{ + if (cursor > m_itemsPerPage - 1) cursor = m_itemsPerPage - 1; + if (cursor < 0) cursor = 0; + if (!m_wasReset) + g_infoManager.SetContainerMoving(GetID(), cursor - m_cursor); + m_cursor = cursor; +} + +void CGUIListContainer::SelectItem(int item) +{ + // Check that m_offset is valid + ValidateOffset(); + // only select an item if it's in a valid range + if (item >= 0 && item < (int)m_items.size()) + { + // Select the item requested + if (item >= m_offset && item < m_offset + m_itemsPerPage) + { // the item is on the current page, so don't change it. + SetCursor(item - m_offset); + } + else if (item < m_offset) + { // item is on a previous page - make it the first item on the page + SetCursor(0); + ScrollToOffset(item); + } + else // (item >= m_offset+m_itemsPerPage) + { // item is on a later page - make it the last item on the page + SetCursor(m_itemsPerPage - 1); + ScrollToOffset(item - m_cursor); + } + } +} +//#ifdef PRE_SKIN_VERSION_9_10_COMPATIBILITY +CGUIListContainer::CGUIListContainer(int parentID, int controlID, float posX, float posY, float width, float height, + const CLabelInfo& labelInfo, const CLabelInfo& labelInfo2, + const CTextureInfo& textureButton, const CTextureInfo& textureButtonFocus, + float textureHeight, float itemWidth, float itemHeight, float spaceBetweenItems) +: CGUIBaseContainer(parentID, controlID, posX, posY, width, height, VERTICAL, 200, 0) +{ + CGUIListItemLayout layout; + layout.CreateListControlLayouts(width, textureHeight + spaceBetweenItems, false, labelInfo, labelInfo2, textureButton, textureButtonFocus, textureHeight, itemWidth, itemHeight, 0, 0); + m_layouts.push_back(layout); + CStdString condition; + condition.Format("control.hasfocus(%i)", controlID); + CStdString condition2 = "!" + condition; + CGUIListItemLayout focusLayout; + focusLayout.CreateListControlLayouts(width, textureHeight + spaceBetweenItems, true, labelInfo, labelInfo2, textureButton, textureButtonFocus, textureHeight, itemWidth, itemHeight, g_infoManager.TranslateString(condition2), g_infoManager.TranslateString(condition)); + m_focusedLayouts.push_back(focusLayout); + m_height = floor(m_height / (textureHeight + spaceBetweenItems)) * (textureHeight + spaceBetweenItems); + ControlType = GUICONTAINER_LIST; +} +//#endif + +bool CGUIListContainer::HasNextPage() const +{ + return (m_offset != (int)m_items.size() - m_itemsPerPage && (int)m_items.size() >= m_itemsPerPage); +} + +bool CGUIListContainer::HasPreviousPage() const +{ + return (m_offset > 0); +} diff --git a/guilib/GUIListContainer.h b/guilib/GUIListContainer.h new file mode 100644 index 0000000000..176183cb1a --- /dev/null +++ b/guilib/GUIListContainer.h @@ -0,0 +1,62 @@ +/*! +\file GUIListContainer.h +\brief +*/ + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIBaseContainer.h" + +/*! + \ingroup controls + \brief + */ +class CGUIListContainer : public CGUIBaseContainer +{ +public: + CGUIListContainer(int parentID, int controlID, float posX, float posY, float width, float height, ORIENTATION orientation, int scrollTime, int preloadItems); +//#ifdef PRE_SKIN_VERSION_9_10_COMPATIBILITY + CGUIListContainer(int parentID, int controlID, float posX, float posY, float width, float height, + const CLabelInfo& labelInfo, const CLabelInfo& labelInfo2, + const CTextureInfo& textureButton, const CTextureInfo& textureButtonFocus, + float textureHeight, float itemWidth, float itemHeight, float spaceBetweenItems); +//#endif + virtual ~CGUIListContainer(void); + virtual CGUIListContainer *Clone() const { return new CGUIListContainer(*this); }; + + virtual bool OnAction(const CAction &action); + virtual bool OnMessage(CGUIMessage& message); + + virtual bool HasNextPage() const; + virtual bool HasPreviousPage() const; + +protected: + virtual void Scroll(int amount); + void SetCursor(int cursor); + virtual bool MoveDown(bool wrapAround); + virtual bool MoveUp(bool wrapAround); + virtual void ValidateOffset(); + virtual void SelectItem(int item); +}; + diff --git a/guilib/GUIListGroup.cpp b/guilib/GUIListGroup.cpp new file mode 100644 index 0000000000..1e093bc345 --- /dev/null +++ b/guilib/GUIListGroup.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIListGroup.h" +#include "GUIListLabel.h" +#include "GUIMultiSelectText.h" +#include "GUIBorderedImage.h" +#include "GUIControlProfiler.h" +#include "utils/log.h" + +CGUIListGroup::CGUIListGroup(int parentID, int controlID, float posX, float posY, float width, float height) +: CGUIControlGroup(parentID, controlID, posX, posY, width, height) +{ + m_item = NULL; + ControlType = GUICONTROL_LISTGROUP; +} + +CGUIListGroup::CGUIListGroup(const CGUIListGroup &right) +: CGUIControlGroup(right) +{ + m_item = NULL; + ControlType = GUICONTROL_LISTGROUP; +} + +CGUIListGroup::~CGUIListGroup(void) +{ + FreeResources(); +} + +void CGUIListGroup::AddControl(CGUIControl *control, int position /*= -1*/) +{ + if (control) + { + if (!(control->GetControlType() == CGUIControl::GUICONTROL_LISTLABEL || + control->GetControlType() == CGUIControl::GUICONTROL_LISTGROUP || + control->GetControlType() == CGUIControl::GUICONTROL_IMAGE || + control->GetControlType() == CGUIControl::GUICONTROL_BORDEREDIMAGE || + control->GetControlType() == CGUIControl::GUICONTROL_MULTISELECT || + control->GetControlType() == CGUIControl::GUICONTROL_TEXTBOX)) + CLog::Log(LOGWARNING, "Trying to add unsupported control type %d", control->GetControlType()); + control->SetPushUpdates(true); + } + CGUIControlGroup::AddControl(control, position); +} + +void CGUIListGroup::Render() +{ + g_graphicsContext.SetOrigin(m_posX, m_posY); + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl *control = *it; + GUIPROFILER_VISIBILITY_BEGIN(control); + control->UpdateVisibility(m_item); + GUIPROFILER_VISIBILITY_END(control); + control->DoRender(m_renderTime); + } + CGUIControl::Render(); + g_graphicsContext.RestoreOrigin(); + m_item = NULL; +} + +void CGUIListGroup::ResetAnimation(ANIMATION_TYPE type) +{ + CGUIControl::ResetAnimation(type); + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + (*it)->ResetAnimation(type); +} + +void CGUIListGroup::UpdateVisibility(const CGUIListItem *item) +{ + CGUIControlGroup::UpdateVisibility(item); + m_item = item; +} + +void CGUIListGroup::UpdateInfo(const CGUIListItem *item) +{ + for (iControls it = m_children.begin(); it != m_children.end(); it++) + (*it)->UpdateInfo(item); + // now we have to check our overlapping label pairs + for (unsigned int i = 0; i < m_children.size(); i++) + { + if (m_children[i]->GetControlType() == CGUIControl::GUICONTROL_LISTLABEL && m_children[i]->IsVisible()) + { + CGUIListLabel &label1 = *(CGUIListLabel *)m_children[i]; + CRect rect1(label1.GetRenderRect()); + for (unsigned int j = i + 1; j < m_children.size(); j++) + { + if (m_children[j]->GetControlType() == CGUIControl::GUICONTROL_LISTLABEL && m_children[j]->IsVisible()) + { // ok, now check if they overlap + CGUIListLabel &label2 = *(CGUIListLabel *)m_children[j]; + if (!rect1.Intersect(label2.GetRenderRect()).IsEmpty()) + { // overlap vertically and horizontally - check alignment + CGUIListLabel &left = label1.GetRenderRect().x1 < label2.GetRenderRect().x1 ? label1 : label2; + CGUIListLabel &right = label1.GetRenderRect().x1 < label2.GetRenderRect().x1 ? label2 : label1; + if ((left.GetLabelInfo().align & 3) == 0 && right.GetLabelInfo().align & XBFONT_RIGHT) + { + float chopPoint = (left.GetXPosition() + left.GetWidth() + right.GetXPosition() - right.GetWidth()) * 0.5f; +// [1 [2...[2 1].|..........1] 2] +// [1 [2.....[2 | 1]..1] 2] +// [1 [2..........|.[2 1]..1] 2] + CRect leftRect(left.GetRenderRect()); + CRect rightRect(right.GetRenderRect()); + if (rightRect.x1 > chopPoint) + chopPoint = rightRect.x1 - 5; + else if (leftRect.x2 < chopPoint) + chopPoint = leftRect.x2 + 5; + leftRect.x2 = chopPoint - 5; + rightRect.x1 = chopPoint + 5; + left.SetRenderRect(leftRect); + right.SetRenderRect(rightRect); + } + } + } + } + } + } +} + +void CGUIListGroup::SetFocusedItem(unsigned int focus) +{ + for (iControls it = m_children.begin(); it != m_children.end(); it++) + { + if ((*it)->GetControlType() == CGUIControl::GUICONTROL_MULTISELECT) + ((CGUIMultiSelectTextControl *)(*it))->SetFocusedItem(focus); + else if ((*it)->GetControlType() == CGUIControl::GUICONTROL_LISTGROUP) + ((CGUIListGroup *)(*it))->SetFocusedItem(focus); + else + (*it)->SetFocus(focus > 0); + } +} + +unsigned int CGUIListGroup::GetFocusedItem() const +{ + for (ciControls it = m_children.begin(); it != m_children.end(); it++) + { + if ((*it)->GetControlType() == CGUIControl::GUICONTROL_MULTISELECT && ((CGUIMultiSelectTextControl *)(*it))->GetFocusedItem()) + return ((CGUIMultiSelectTextControl *)(*it))->GetFocusedItem(); + else if ((*it)->GetControlType() == CGUIControl::GUICONTROL_LISTGROUP && ((CGUIListGroup *)(*it))->GetFocusedItem()) + return ((CGUIListGroup *)(*it))->GetFocusedItem(); + } + return 0; +} + +bool CGUIListGroup::MoveLeft() +{ + for (iControls it = m_children.begin(); it != m_children.end(); it++) + { + if ((*it)->GetControlType() == CGUIControl::GUICONTROL_MULTISELECT && ((CGUIMultiSelectTextControl *)(*it))->MoveLeft()) + return true; + else if ((*it)->GetControlType() == CGUIControl::GUICONTROL_LISTGROUP && ((CGUIListGroup *)(*it))->MoveLeft()) + return true; + } + return false; +} + +bool CGUIListGroup::MoveRight() +{ + for (iControls it = m_children.begin(); it != m_children.end(); it++) + { + if ((*it)->GetControlType() == CGUIControl::GUICONTROL_MULTISELECT && ((CGUIMultiSelectTextControl *)(*it))->MoveLeft()) + return true; + else if ((*it)->GetControlType() == CGUIControl::GUICONTROL_LISTGROUP && ((CGUIListGroup *)(*it))->MoveLeft()) + return true; + } + return false; +} + +void CGUIListGroup::SetState(bool selected, bool focused) +{ + for (iControls it = m_children.begin(); it != m_children.end(); it++) + { + if ((*it)->GetControlType() == CGUIControl::GUICONTROL_LISTLABEL) + { + CGUIListLabel *label = (CGUIListLabel *)(*it); + label->SetSelected(selected); + label->SetScrolling(focused); + } + } +} + +void CGUIListGroup::SelectItemFromPoint(const CPoint &point) +{ + CPoint controlCoords(point); + m_transform.InverseTransformPosition(controlCoords.x, controlCoords.y); + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl *child = *it; + if (child->GetControlType() == CGUIControl::GUICONTROL_MULTISELECT) + ((CGUIMultiSelectTextControl *)child)->SelectItemFromPoint(point); + else if (child->GetControlType() == CGUIControl::GUICONTROL_LISTGROUP) + ((CGUIListGroup *)child)->SelectItemFromPoint(point); + } +} diff --git a/guilib/GUIListGroup.h b/guilib/GUIListGroup.h new file mode 100644 index 0000000000..241ca229f4 --- /dev/null +++ b/guilib/GUIListGroup.h @@ -0,0 +1,60 @@ +/*! +\file GUIListGroup.h +\brief +*/ + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControlGroup.h" + +/*! + \ingroup controls + \brief a group of controls within a list/panel container + */ +class CGUIListGroup : public CGUIControlGroup +{ +public: + CGUIListGroup(int parentID, int controlID, float posX, float posY, float width, float height); + CGUIListGroup(const CGUIListGroup &right); + virtual ~CGUIListGroup(void); + virtual CGUIListGroup *Clone() const { return new CGUIListGroup(*this); }; + + virtual void AddControl(CGUIControl *control, int position = -1); + + virtual void Render(); + virtual void ResetAnimation(ANIMATION_TYPE type); + virtual void UpdateVisibility(const CGUIListItem *item = NULL); + virtual void UpdateInfo(const CGUIListItem *item); + + void SetFocusedItem(unsigned int subfocus); + unsigned int GetFocusedItem() const; + bool MoveLeft(); + bool MoveRight(); + void SetState(bool selected, bool focused); + void SelectItemFromPoint(const CPoint &point); + +protected: + const CGUIListItem *m_item; +}; + diff --git a/guilib/GUIListItem.cpp b/guilib/GUIListItem.cpp new file mode 100644 index 0000000000..9aabebfe69 --- /dev/null +++ b/guilib/GUIListItem.cpp @@ -0,0 +1,382 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIListItem.h" +#include "GUIListItemLayout.h" +#include "utils/Archive.h" + +CGUIListItem::CGUIListItem(const CGUIListItem& item) +{ + *this = item; + SetInvalid(); +} + +CGUIListItem::CGUIListItem(void) +{ + m_bIsFolder = false; + m_strLabel2 = ""; + m_strLabel = ""; + m_bSelected = false; + m_strIcon = ""; + m_strThumbnailImage = ""; + m_overlayIcon = ICON_OVERLAY_NONE; + m_layout = NULL; + m_focusedLayout = NULL; +} + +CGUIListItem::CGUIListItem(const CStdString& strLabel) +{ + m_bIsFolder = false; + m_strLabel2 = ""; + m_strLabel = strLabel; + m_sortLabel = strLabel; + m_bSelected = false; + m_strIcon = ""; + m_strThumbnailImage = ""; + m_overlayIcon = ICON_OVERLAY_NONE; + m_layout = NULL; + m_focusedLayout = NULL; +} + +CGUIListItem::~CGUIListItem(void) +{ + FreeMemory(); +} + +void CGUIListItem::SetLabel(const CStdString& strLabel) +{ + m_strLabel = strLabel; + if (m_sortLabel.IsEmpty()) + m_sortLabel = strLabel; + SetInvalid(); +} + +const CStdString& CGUIListItem::GetLabel() const +{ + return m_strLabel; +} + + +void CGUIListItem::SetLabel2(const CStdString& strLabel2) +{ + m_strLabel2 = strLabel2; + SetInvalid(); +} + +const CStdString& CGUIListItem::GetLabel2() const +{ + return m_strLabel2; +} + +void CGUIListItem::SetSortLabel(const CStdString &label) +{ + m_sortLabel = label; + // no need to invalidate - this is never shown in the UI +} + +const CStdString& CGUIListItem::GetSortLabel() const +{ + return m_sortLabel; +} + +void CGUIListItem::SetThumbnailImage(const CStdString& strThumbnail) +{ + m_strThumbnailImage = strThumbnail; + SetInvalid(); +} + +const CStdString& CGUIListItem::GetThumbnailImage() const +{ + return m_strThumbnailImage; +} + +void CGUIListItem::SetIconImage(const CStdString& strIcon) +{ + m_strIcon = strIcon; + SetInvalid(); +} + +const CStdString& CGUIListItem::GetIconImage() const +{ + return m_strIcon; +} + +void CGUIListItem::SetOverlayImage(GUIIconOverlay icon, bool bOnOff) +{ + if (bOnOff) + m_overlayIcon = GUIIconOverlay((int)(icon)+1); + else + m_overlayIcon = icon; + SetInvalid(); +} + +CStdString CGUIListItem::GetOverlayImage() const +{ + switch (m_overlayIcon) + { + case ICON_OVERLAY_RAR: + return "OverlayRAR.png"; + case ICON_OVERLAY_ZIP: + return "OverlayZIP.png"; + case ICON_OVERLAY_TRAINED: + return "OverlayTrained.png"; + case ICON_OVERLAY_HAS_TRAINER: + return "OverlayHasTrainer.png"; + case ICON_OVERLAY_LOCKED: + return "OverlayLocked.png"; + case ICON_OVERLAY_UNWATCHED: + return "OverlayUnwatched.png"; + case ICON_OVERLAY_WATCHED: + return "OverlayWatched.png"; + case ICON_OVERLAY_HD: + return "OverlayHD.png"; + default: + return ""; + } +} + +void CGUIListItem::Select(bool bOnOff) +{ + m_bSelected = bOnOff; +} + +bool CGUIListItem::HasIcon() const +{ + return (m_strIcon.size() != 0); +} + + +bool CGUIListItem::HasThumbnail() const +{ + return (m_strThumbnailImage.size() != 0); +} + +bool CGUIListItem::HasOverlay() const +{ + return (m_overlayIcon != CGUIListItem::ICON_OVERLAY_NONE); +} + +bool CGUIListItem::IsSelected() const +{ + return m_bSelected; +} + +const CGUIListItem& CGUIListItem::operator =(const CGUIListItem& item) +{ + if (&item == this) return * this; + m_strLabel2 = item.m_strLabel2; + m_strLabel = item.m_strLabel; + m_sortLabel = item.m_sortLabel; + FreeMemory(); + m_bSelected = item.m_bSelected; + m_strIcon = item.m_strIcon; + m_strThumbnailImage = item.m_strThumbnailImage; + m_overlayIcon = item.m_overlayIcon; + m_bIsFolder = item.m_bIsFolder; + m_mapProperties = item.m_mapProperties; + SetInvalid(); + return *this; +} + +void CGUIListItem::Serialize(CArchive &ar) +{ + if (ar.IsStoring()) + { + ar << m_bIsFolder; + ar << m_strLabel; + ar << m_strLabel2; + ar << m_sortLabel; + ar << m_strThumbnailImage; + ar << m_strIcon; + ar << m_bSelected; + ar << m_overlayIcon; + ar << (int)m_mapProperties.size(); + for (std::map<CStdString, CStdString, icompare>::const_iterator it = m_mapProperties.begin(); it != m_mapProperties.end(); it++) + { + ar << it->first; + ar << it->second; + } + } + else + { + ar >> m_bIsFolder; + ar >> m_strLabel; + ar >> m_strLabel2; + ar >> m_sortLabel; + ar >> m_strThumbnailImage; + ar >> m_strIcon; + ar >> m_bSelected; + + int overlayIcon; + ar >> overlayIcon; + m_overlayIcon = GUIIconOverlay(overlayIcon); + + int mapSize; + ar >> mapSize; + for (int i = 0; i < mapSize; i++) + { + CStdString key, value; + ar >> key; + ar >> value; + SetProperty(key, value); + } + } +} + +void CGUIListItem::FreeIcons() +{ + FreeMemory(); + m_strThumbnailImage = ""; + m_strIcon = ""; + SetInvalid(); +} + +void CGUIListItem::FreeMemory() +{ + if (m_layout) + { + delete m_layout; + m_layout = NULL; + } + if (m_focusedLayout) + { + delete m_focusedLayout; + m_focusedLayout = NULL; + } +} + +void CGUIListItem::SetLayout(CGUIListItemLayout *layout) +{ + delete m_layout; + m_layout = layout; +} + +CGUIListItemLayout *CGUIListItem::GetLayout() +{ + return m_layout; +} + +void CGUIListItem::SetFocusedLayout(CGUIListItemLayout *layout) +{ + delete m_focusedLayout; + m_focusedLayout = layout; +} + +CGUIListItemLayout *CGUIListItem::GetFocusedLayout() +{ + return m_focusedLayout; +} + +void CGUIListItem::SetInvalid() +{ + if (m_layout) m_layout->SetInvalid(); + if (m_focusedLayout) m_focusedLayout->SetInvalid(); +} + +void CGUIListItem::SetProperty(const CStdString &strKey, const char *strValue) +{ + m_mapProperties[strKey] = strValue; +} + +void CGUIListItem::SetProperty(const CStdString &strKey, const CStdString &strValue) +{ + m_mapProperties[strKey] = strValue; +} + +CStdString CGUIListItem::GetProperty(const CStdString &strKey) const +{ + std::map<CStdString,CStdString,icompare>::const_iterator iter = m_mapProperties.find(strKey); + if (iter == m_mapProperties.end()) + return ""; + + return iter->second; +} + +bool CGUIListItem::HasProperty(const CStdString &strKey) const +{ + std::map<CStdString,CStdString,icompare>::const_iterator iter = m_mapProperties.find(strKey); + if (iter == m_mapProperties.end()) + return false; + + return true; +} + +void CGUIListItem::ClearProperty(const CStdString &strKey) +{ + std::map<CStdString,CStdString,icompare>::iterator iter = m_mapProperties.find(strKey); + if (iter != m_mapProperties.end()) + m_mapProperties.erase(iter); +} + +void CGUIListItem::ClearProperties() +{ + m_mapProperties.clear(); +} + +void CGUIListItem::SetProperty(const CStdString &strKey, int nVal) +{ + CStdString strVal; + strVal.Format("%d",nVal); + SetProperty(strKey, strVal); +} + +void CGUIListItem::IncrementProperty(const CStdString &strKey, int nVal) +{ + int i = GetPropertyInt(strKey); + i += nVal; + SetProperty(strKey, i); +} + +void CGUIListItem::SetProperty(const CStdString &strKey, bool bVal) +{ + SetProperty(strKey, bVal?"1":"0"); +} + +void CGUIListItem::SetProperty(const CStdString &strKey, double dVal) +{ + CStdString strVal; + strVal.Format("%f",dVal); + SetProperty(strKey, strVal); +} + +void CGUIListItem::IncrementProperty(const CStdString &strKey, double dVal) +{ + double d = GetPropertyDouble(strKey); + d += dVal; + SetProperty(strKey, d); +} + +bool CGUIListItem::GetPropertyBOOL(const CStdString &strKey) const +{ + return GetProperty(strKey) == "1"; +} + +int CGUIListItem::GetPropertyInt(const CStdString &strKey) const +{ + return atoi(GetProperty(strKey).c_str()) ; +} + +double CGUIListItem::GetPropertyDouble(const CStdString &strKey) const +{ + return atof(GetProperty(strKey).c_str()) ; +} + + diff --git a/guilib/GUIListItem.h b/guilib/GUIListItem.h new file mode 100644 index 0000000000..7384410812 --- /dev/null +++ b/guilib/GUIListItem.h @@ -0,0 +1,150 @@ +/*! +\file GUIListItem.h +\brief +*/ + +#ifndef GUILIB_GUILISTITEM_H +#define GUILIB_GUILISTITEM_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "StdString.h" + +#include <map> +#include <string> + +// Forward +class CGUIListItemLayout; +class CArchive; + +/*! + \ingroup controls + \brief + */ +class CGUIListItem +{ +public: + enum GUIIconOverlay { ICON_OVERLAY_NONE = 0, + ICON_OVERLAY_RAR, + ICON_OVERLAY_ZIP, + ICON_OVERLAY_LOCKED, + ICON_OVERLAY_HAS_TRAINER, + ICON_OVERLAY_TRAINED, + ICON_OVERLAY_UNWATCHED, + ICON_OVERLAY_WATCHED, + ICON_OVERLAY_HD}; + + CGUIListItem(void); + CGUIListItem(const CGUIListItem& item); + CGUIListItem(const CStdString& strLabel); + virtual ~CGUIListItem(void); + + + const CGUIListItem& operator =(const CGUIListItem& item); + + virtual void SetLabel(const CStdString& strLabel); + const CStdString& GetLabel() const; + + void SetLabel2(const CStdString& strLabel); + const CStdString& GetLabel2() const; + + void SetIconImage(const CStdString& strIcon); + const CStdString& GetIconImage() const; + + void SetThumbnailImage(const CStdString& strThumbnail); + const CStdString& GetThumbnailImage() const; + + void SetOverlayImage(GUIIconOverlay icon, bool bOnOff=false); + CStdString GetOverlayImage() const; + + void SetSortLabel(const CStdString &label); + const CStdString &GetSortLabel() const; + + void Select(bool bOnOff); + bool IsSelected() const; + + bool HasIcon() const; + bool HasThumbnail() const; + bool HasOverlay() const; + virtual bool IsFileItem() const { return false; }; + + void SetLayout(CGUIListItemLayout *layout); + CGUIListItemLayout *GetLayout(); + + void SetFocusedLayout(CGUIListItemLayout *layout); + CGUIListItemLayout *GetFocusedLayout(); + + void FreeIcons(); + void FreeMemory(); + void SetInvalid(); + + bool m_bIsFolder; ///< is item a folder or a file + + void SetProperty(const CStdString &strKey, const char *strValue); + void SetProperty(const CStdString &strKey, const CStdString &strValue); + void SetProperty(const CStdString &strKey, int nVal); + void SetProperty(const CStdString &strKey, bool bVal); + void SetProperty(const CStdString &strKey, double dVal); + + void IncrementProperty(const CStdString &strKey, int nVal); + void IncrementProperty(const CStdString &strKey, double dVal); + + void ClearProperties(); + + void Serialize(CArchive& ar); + + bool HasProperty(const CStdString &strKey) const; + bool HasProperties() const { return m_mapProperties.size() > 0; }; + void ClearProperty(const CStdString &strKey); + + CStdString GetProperty(const CStdString &strKey) const; + bool GetPropertyBOOL(const CStdString &strKey) const; + int GetPropertyInt(const CStdString &strKey) const; + double GetPropertyDouble(const CStdString &strKey) const; + +protected: + CStdString m_strLabel2; // text of column2 + CStdString m_strThumbnailImage; // filename of thumbnail + CStdString m_strIcon; // filename of icon + GUIIconOverlay m_overlayIcon; // type of overlay icon + + CGUIListItemLayout *m_layout; + CGUIListItemLayout *m_focusedLayout; + bool m_bSelected; // item is selected or not + + struct icompare + { + bool operator()(const CStdString &s1, const CStdString &s2) const + { + return s1.CompareNoCase(s2) < 0; + } + }; + + std::map<CStdString, CStdString, icompare> m_mapProperties; +private: + CStdString m_sortLabel; // text for sorting + CStdString m_strLabel; // text of column1 +}; +#endif + diff --git a/guilib/GUIListItemLayout.cpp b/guilib/GUIListItemLayout.cpp new file mode 100644 index 0000000000..7f5eae3da4 --- /dev/null +++ b/guilib/GUIListItemLayout.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIListItemLayout.h" +#include "FileItem.h" +#include "GUIControlFactory.h" +#include "SkinInfo.h" +#include "utils/GUIInfoManager.h" +#include "GUIListLabel.h" +#include "GUIImage.h" +#include "tinyXML/tinyxml.h" + +using namespace std; + +CGUIListItemLayout::CGUIListItemLayout() +: m_group(0, 0, 0, 0, 0, 0) +{ + m_width = 0; + m_height = 0; + m_condition = 0; + m_focused = false; + m_invalidated = true; + m_isPlaying = false; +} + +CGUIListItemLayout::CGUIListItemLayout(const CGUIListItemLayout &from) +: m_group(from.m_group) +{ + m_width = from.m_width; + m_height = from.m_height; + m_focused = from.m_focused; + m_condition = from.m_condition; + m_invalidated = true; + m_isPlaying = false; +} + +CGUIListItemLayout::~CGUIListItemLayout() +{ +} + +bool CGUIListItemLayout::IsAnimating(ANIMATION_TYPE animType) +{ + return m_group.IsAnimating(animType); +} + +void CGUIListItemLayout::ResetAnimation(ANIMATION_TYPE animType) +{ + return m_group.ResetAnimation(animType); +} + +float CGUIListItemLayout::Size(ORIENTATION orientation) const +{ + return (orientation == HORIZONTAL) ? m_width : m_height; +} + +void CGUIListItemLayout::Render(CGUIListItem *item, int parentID, DWORD time) +{ + if (m_invalidated) + { // need to update our item + // could use a dynamic cast here if RTTI was enabled. As it's not, + // let's use a static cast with a virtual base function + CFileItem *fileItem = item->IsFileItem() ? (CFileItem *)item : new CFileItem(*item); + m_isPlaying = g_infoManager.GetBool(LISTITEM_ISPLAYING, parentID, item); + m_group.SetInvalid(); + m_group.UpdateInfo(fileItem); + m_invalidated = false; + // delete our temporary fileitem + if (!item->IsFileItem()) + delete fileItem; + } + + // update visibility, and render + m_group.SetState(item->IsSelected() || m_isPlaying, m_focused); + m_group.UpdateVisibility(item); + m_group.DoRender(time); +} + +void CGUIListItemLayout::SetFocusedItem(unsigned int focus) +{ + m_group.SetFocusedItem(focus); +} + +unsigned int CGUIListItemLayout::GetFocusedItem() const +{ + return m_group.GetFocusedItem(); +} + +void CGUIListItemLayout::SelectItemFromPoint(const CPoint &point) +{ + m_group.SelectItemFromPoint(point); +} + +bool CGUIListItemLayout::MoveLeft() +{ + return m_group.MoveLeft(); +} + +bool CGUIListItemLayout::MoveRight() +{ + return m_group.MoveRight(); +} + +void CGUIListItemLayout::LoadControl(TiXmlElement *child, CGUIControlGroup *group) +{ + if (!group) return; + + FRECT rect = { group->GetXPosition(), group->GetYPosition(), group->GetXPosition() + group->GetWidth(), group->GetYPosition() + group->GetHeight() }; + + CGUIControlFactory factory; + CGUIControl *control = factory.Create(0, rect, child, true); // true indicating we're inside a list for the + // different label control + defaults. + if (control) + { + group->AddControl(control); + if (control->IsGroup()) + { + TiXmlElement *grandChild = child->FirstChildElement("control"); + while (grandChild) + { + LoadControl(grandChild, (CGUIControlGroup *)control); + grandChild = grandChild->NextSiblingElement("control"); + } + } + } +} + +void CGUIListItemLayout::LoadLayout(TiXmlElement *layout, bool focused) +{ + m_focused = focused; + g_SkinInfo.ResolveIncludes(layout); + g_SkinInfo.ResolveConstant(layout->Attribute("width"), m_width); + g_SkinInfo.ResolveConstant(layout->Attribute("height"), m_height); + const char *condition = layout->Attribute("condition"); + if (condition) + m_condition = g_infoManager.TranslateString(condition); + TiXmlElement *child = layout->FirstChildElement("control"); + m_group.SetWidth(m_width); + m_group.SetHeight(m_height); + while (child) + { + LoadControl(child, &m_group); + child = child->NextSiblingElement("control"); + } +} + +//#ifdef PRE_SKIN_VERSION_9_10_COMPATIBILITY +void CGUIListItemLayout::CreateListControlLayouts(float width, float height, bool focused, const CLabelInfo &labelInfo, const CLabelInfo &labelInfo2, const CTextureInfo &texture, const CTextureInfo &textureFocus, float texHeight, float iconWidth, float iconHeight, int nofocusCondition, int focusCondition) +{ + m_width = width; + m_height = height; + m_focused = focused; + CGUIImage *tex = new CGUIImage(0, 0, 0, 0, width, texHeight, texture); + tex->SetVisibleCondition(nofocusCondition, false); + m_group.AddControl(tex); + if (focused) + { + CGUIImage *tex = new CGUIImage(0, 0, 0, 0, width, texHeight, textureFocus); + tex->SetVisibleCondition(focusCondition, false); + m_group.AddControl(tex); + } + CGUIImage *image = new CGUIImage(0, 0, 8, 0, iconWidth, texHeight, CTextureInfo("")); + image->SetInfo(CGUIInfoLabel("$INFO[ListItem.Icon]")); + image->SetAspectRatio(CAspectRatio::AR_KEEP); + m_group.AddControl(image); + float x = iconWidth + labelInfo.offsetX + 10; + CGUIListLabel *label = new CGUIListLabel(0, 0, x, labelInfo.offsetY, width - x - 18, height, labelInfo, CGUIInfoLabel("$INFO[ListItem.Label]"), false, CScrollInfo::defaultSpeed); + m_group.AddControl(label); + x = labelInfo2.offsetX ? labelInfo2.offsetX : m_width - 16; + label = new CGUIListLabel(0, 0, x, labelInfo2.offsetY, x - iconWidth - 20, height, labelInfo2, CGUIInfoLabel("$INFO[ListItem.Label2]"), false, CScrollInfo::defaultSpeed); + m_group.AddControl(label); +} +//#endif + +#ifdef _DEBUG +void CGUIListItemLayout::DumpTextureUse() +{ + m_group.DumpTextureUse(); +} +#endif diff --git a/guilib/GUIListItemLayout.h b/guilib/GUIListItemLayout.h new file mode 100644 index 0000000000..f0c03ba641 --- /dev/null +++ b/guilib/GUIListItemLayout.h @@ -0,0 +1,71 @@ +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIListGroup.h" +#include "GUITexture.h" + +class CGUIListItem; +class CFileItem; + +class CGUIListItemLayout +{ +public: + CGUIListItemLayout(); + CGUIListItemLayout(const CGUIListItemLayout &from); + virtual ~CGUIListItemLayout(); + void LoadLayout(TiXmlElement *layout, bool focused); + void Render(CGUIListItem *item, int parentID, DWORD time = 0); + float Size(ORIENTATION orientation) const; + unsigned int GetFocusedItem() const; + void SetFocusedItem(unsigned int focus); + bool IsAnimating(ANIMATION_TYPE animType); + void ResetAnimation(ANIMATION_TYPE animType); + void SetInvalid() { m_invalidated = true; }; + +//#ifdef PRE_SKIN_VERSION_9_10_COMPATIBILITY + void CreateListControlLayouts(float width, float height, bool focused, const CLabelInfo &labelInfo, const CLabelInfo &labelInfo2, const CTextureInfo &texture, const CTextureInfo &textureFocus, float texHeight, float iconWidth, float iconHeight, int nofocusCondition, int focusCondition); +//#endif + + void SelectItemFromPoint(const CPoint &point); + bool MoveLeft(); + bool MoveRight(); + + int GetCondition() const { return m_condition; }; +#ifdef _DEBUG + virtual void DumpTextureUse(); +#endif +protected: + void LoadControl(TiXmlElement *child, CGUIControlGroup *group); + void Update(CFileItem *item); + + CGUIListGroup m_group; + + float m_width; + float m_height; + bool m_focused; + bool m_invalidated; + + int m_condition; + bool m_isPlaying; +}; + diff --git a/guilib/GUIListLabel.cpp b/guilib/GUIListLabel.cpp new file mode 100644 index 0000000000..b08d473549 --- /dev/null +++ b/guilib/GUIListLabel.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIListLabel.h" +#include "utils/CharsetConverter.h" +#include <limits> + +CGUIListLabel::CGUIListLabel(int parentID, int controlID, float posX, float posY, float width, float height, const CLabelInfo& labelInfo, const CGUIInfoLabel &info, bool alwaysScroll, int scrollSpeed) + : CGUIControl(parentID, controlID, posX, posY, width, height) + , m_textLayout(labelInfo.font, false) + , m_scrollInfo(50, 0, scrollSpeed) + , m_renderRect() +{ + m_selected = false; + m_scrolling = m_alwaysScroll = alwaysScroll; + m_label = labelInfo; + m_info = info; + m_textWidth = width; + if (m_info.IsConstant()) + SetLabel(m_info.GetLabel(m_parentID, true)); + ControlType = GUICONTROL_LISTLABEL; +} + +CGUIListLabel::~CGUIListLabel(void) +{ +} + +void CGUIListLabel::SetScrolling(bool scrolling) +{ + m_scrolling = m_alwaysScroll ? true : scrolling; + if (!m_scrolling) + m_scrollInfo.Reset(); +} + +void CGUIListLabel::SetSelected(bool selected) +{ + m_selected = selected; +} + +void CGUIListLabel::SetFocus(bool focus) +{ + CGUIControl::SetFocus(focus); + if (!focus) + SetScrolling(false); +} + +void CGUIListLabel::UpdateColors() +{ + m_label.UpdateColors(); + CGUIControl::UpdateColors(); +} + +void CGUIListLabel::Render() +{ + color_t color = m_selected ? m_label.selectedColor : m_label.textColor; + bool needsToScroll = (m_renderRect.Width() + 0.5f < m_textWidth); // 0.5f to deal with floating point rounding issues + if (m_scrolling && needsToScroll) + m_textLayout.RenderScrolling(m_renderRect.x1, m_renderRect.y1, m_label.angle, color, m_label.shadowColor, 0, m_renderRect.Width(), m_scrollInfo); + else + { + float posX = m_renderRect.x1; + uint32_t align = 0; + if (!needsToScroll) + { // hack for right and centered multiline text, as GUITextLayout::Render() treats posX as the right hand + // or center edge of the text (see GUIFontTTF::DrawTextInternal), and this has already been taken care of + // in SetLabel(), but we wish to still pass the horizontal alignment info through (so that multiline text + // is aligned correctly), so we must undo the SetLabel() changes for horizontal alignment. + if (m_label.align & XBFONT_RIGHT) + posX += m_renderRect.Width(); + else if (m_label.align & XBFONT_CENTER_X) + posX += m_renderRect.Width() * 0.5f; + align = m_label.align & ~XBFONT_CENTER_Y; // ignore vertical alignment + } + m_textLayout.Render(posX, m_renderRect.y1, m_label.angle, color, m_label.shadowColor, align, m_renderRect.Width()); + } + CGUIControl::Render(); +} + +void CGUIListLabel::UpdateInfo(const CGUIListItem *item) +{ + if (m_info.IsConstant() && !m_bInvalidated) + return; // nothing to do + + if (item) + SetLabel(m_info.GetItemLabel(item)); + else + SetLabel(m_info.GetLabel(m_parentID, true)); +} + +void CGUIListLabel::SetLabel(const CStdString &label) +{ + if (m_textLayout.Update(label, 0, m_bInvalidated)) + { // needed an update - reset scrolling + m_scrollInfo.Reset(); + // recalculate our text layout + float width, height; + m_textLayout.GetTextExtent(m_textWidth, height); + width = std::min(m_textWidth, m_width); + if (m_label.align & XBFONT_CENTER_Y) + m_renderRect.y1 = m_posY + (m_height - height) * 0.5f; + else + m_renderRect.y1 = m_posY; + if (m_label.align & XBFONT_RIGHT) + m_renderRect.x1 = m_posX - width; + else if (m_label.align & XBFONT_CENTER_X) + m_renderRect.x1 = m_posX - width * 0.5f; + else + m_renderRect.x1 = m_posX; + m_renderRect.x2 = m_renderRect.x1 + width; + m_renderRect.y2 = m_renderRect.y1 + height; + } +} diff --git a/guilib/GUIListLabel.h b/guilib/GUIListLabel.h new file mode 100644 index 0000000000..9ef10c6e79 --- /dev/null +++ b/guilib/GUIListLabel.h @@ -0,0 +1,70 @@ +/*! +\file GUIListLabel.h +\brief +*/ + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControl.h" + +#include "GUILabelControl.h" // for CLabelInfo +/*! + \ingroup controls + \brief + */ +class CGUIListLabel : + public CGUIControl +{ +public: + CGUIListLabel(int parentID, int controlID, float posX, float posY, float width, float height, const CLabelInfo& labelInfo, const CGUIInfoLabel &label, bool alwaysScroll, int scrollSpeed); + virtual ~CGUIListLabel(void); + virtual CGUIListLabel *Clone() const { return new CGUIListLabel(*this); }; + + virtual void Render(); + virtual bool CanFocus() const { return false; }; + virtual void UpdateInfo(const CGUIListItem *item = NULL); + virtual void SetFocus(bool focus); + + const CRect &GetRenderRect() const { return m_renderRect; }; + void SetRenderRect(const CRect &rect) { m_renderRect = rect; }; + void SetLabel(const CStdString &label); + void SetSelected(bool selected); + void SetScrolling(bool scrolling); + + const CLabelInfo& GetLabelInfo() const { return m_label; }; + +protected: + virtual void UpdateColors(); + + CLabelInfo m_label; + CGUITextLayout m_textLayout; + CGUIInfoLabel m_info; + float m_textWidth; + + bool m_scrolling; + bool m_alwaysScroll; + bool m_selected; + CScrollInfo m_scrollInfo; + CRect m_renderRect; // render location +}; diff --git a/guilib/GUIMessage.cpp b/guilib/GUIMessage.cpp new file mode 100644 index 0000000000..6a08bd36b5 --- /dev/null +++ b/guilib/GUIMessage.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIMessage.h" +#include "LocalizeStrings.h" + +using namespace std; + +CStdString CGUIMessage::empty_string; + +CGUIMessage::CGUIMessage(int msg, int senderID, int controlID, int param1, int param2) +{ + m_message = msg; + m_senderID = senderID; + m_controlID = controlID; + m_param1 = param1; + m_param2 = param2; + m_pointer = NULL; +} + +CGUIMessage::CGUIMessage(int msg, int senderID, int controlID, int param1, int param2, CFileItemList *item) +{ + m_message = msg; + m_senderID = senderID; + m_controlID = controlID; + m_param1 = param1; + m_param2 = param2; + m_pointer = item; +} + +CGUIMessage::CGUIMessage(int msg, int senderID, int controlID, int param1, int param2, const CGUIListItemPtr &item) +{ + m_message = msg; + m_senderID = senderID; + m_controlID = controlID; + m_param1 = param1; + m_param2 = param2; + m_pointer = NULL; + m_item = item; +} + +CGUIMessage::CGUIMessage(int msg, int senderID, int controlID, int param1, int param2, CVisualisation* vis) +{ + m_message = msg; + m_senderID = senderID; + m_controlID = controlID; + m_param1 = param1; + m_param2 = param2; + m_pointer = vis; +} + +CGUIMessage::CGUIMessage(const CGUIMessage& msg) +{ + *this = msg; +} + +CGUIMessage::~CGUIMessage(void) +{} + + +int CGUIMessage::GetControlId() const +{ + return m_controlID; +} + +int CGUIMessage::GetMessage() const +{ + return m_message; +} + +void* CGUIMessage::GetPointer() const +{ + return m_pointer; +} + +CGUIListItemPtr CGUIMessage::GetItem() const +{ + return m_item; +} + +int CGUIMessage::GetParam1() const +{ + return m_param1; +} + +int CGUIMessage::GetParam2() const +{ + return m_param2; +} + +int CGUIMessage::GetSenderId() const +{ + return m_senderID; +} + + +const CGUIMessage& CGUIMessage::operator = (const CGUIMessage& msg) +{ + if (this == &msg) return * this; + + m_message = msg.m_message; + m_controlID = msg.m_controlID; + m_param1 = msg.m_param1; + m_param2 = msg.m_param2; + m_pointer = msg.m_pointer; + m_strLabel = msg.m_strLabel; + m_senderID = msg.m_senderID; + m_params = msg.m_params; + m_item = msg.m_item; + m_action = msg.m_action; + return *this; +} + + +void CGUIMessage::SetParam1(int param1) +{ + m_param1 = param1; +} + +void CGUIMessage::SetParam2(int param2) +{ + m_param2 = param2; +} + +void CGUIMessage::SetPointer(void* lpVoid) +{ + m_pointer = lpVoid; +} + +void CGUIMessage::SetLabel(const string& strLabel) +{ + m_strLabel = strLabel; +} + +const string& CGUIMessage::GetLabel() const +{ + return m_strLabel; +} + +void CGUIMessage::SetLabel(int iString) +{ + m_strLabel = g_localizeStrings.Get(iString); +} + +void CGUIMessage::SetStringParam(const CStdString& strParam) +{ + m_params.clear(); + if (strParam.size()) + m_params.push_back(strParam); +} + +void CGUIMessage::SetStringParams(const vector<CStdString> ¶ms) +{ + m_params = params; +} + +const CStdString& CGUIMessage::GetStringParam(size_t param) const +{ + if (param >= m_params.size()) + return empty_string; + return m_params[param]; +} + +size_t CGUIMessage::GetNumStringParams() const +{ + return m_params.size(); +} + +void CGUIMessage::SetAction(const CGUIActionDescriptor& action) +{ + m_action = action; +} + +const CGUIActionDescriptor& CGUIMessage::GetAction() const +{ + return m_action; +} diff --git a/guilib/GUIMessage.h b/guilib/GUIMessage.h new file mode 100644 index 0000000000..c5cf464d00 --- /dev/null +++ b/guilib/GUIMessage.h @@ -0,0 +1,288 @@ +/*! +\file GUIMessage.h +\brief +*/ + +#ifndef GUILIB_MESSAGE_H +#define GUILIB_MESSAGE_H + +#include "GUIActionDescriptor.h" + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#define GUI_MSG_WINDOW_INIT 1 // initialize window +#define GUI_MSG_WINDOW_DEINIT 2 // deinit window +#define GUI_MSG_WINDOW_RESET 27 // reset window to initial state + +#define GUI_MSG_SETFOCUS 3 // set focus to control param1=up/down/left/right +#define GUI_MSG_LOSTFOCUS 4 // control lost focus + +#define GUI_MSG_CLICKED 5 // control has been clicked + +#define GUI_MSG_VISIBLE 6 // set control visible +#define GUI_MSG_HIDDEN 7 // set control hidden + +#define GUI_MSG_ENABLED 8 // enable control +#define GUI_MSG_DISABLED 9 // disable control + +#define GUI_MSG_SELECTED 10 // control = selected +#define GUI_MSG_DESELECTED 11 // control = not selected + +#define GUI_MSG_LABEL_ADD 12 // add label control (for controls supporting more then 1 label) + +#define GUI_MSG_LABEL_SET 13 // set the label of a control + +#define GUI_MSG_LABEL_RESET 14 // clear all labels of a control // add label control (for controls supporting more then 1 label) + +#define GUI_MSG_ITEM_SELECTED 15 // ask control 2 return the selected item +#define GUI_MSG_ITEM_SELECT 16 // ask control 2 select a specific item +#define GUI_MSG_LABEL2_SET 17 +#define GUI_MSG_SHOWRANGE 18 + +#define GUI_MSG_FULLSCREEN 19 // should go to fullscreen window (vis or video) +#define GUI_MSG_EXECUTE 20 // user has clicked on a button with <execute> tag + +#define GUI_MSG_NOTIFY_ALL 21 // message will be send to all active and inactive(!) windows, all active modal and modeless dialogs + // dwParam1 must contain an additional message the windows should react on + +#define GUI_MSG_REFRESH_THUMBS 22 // message is sent to all windows to refresh all thumbs + +#define GUI_MSG_MOVE 23 // message is sent to the window from the base control class when it's + // been asked to move. dwParam1 contains direction. + +#define GUI_MSG_LABEL_BIND 24 // bind label control (for controls supporting more then 1 label) + +#define GUI_MSG_SELCHANGED 25 // selection within the control has changed + +#define GUI_MSG_FOCUSED 26 // a control has become focused + +#define GUI_MSG_PAGE_CHANGE 28 // a page control has changed the page number + +#define GUI_MSG_REFRESH_LIST 29 // message sent to all listing controls telling them to refresh their item layouts + +#define GUI_MSG_PAGE_UP 30 // page up +#define GUI_MSG_PAGE_DOWN 31 // page down +#define GUI_MSG_MOVE_OFFSET 32 // Instruct the contorl to MoveUp or MoveDown by offset amount + +#define GUI_MSG_SET_TYPE 33 ///< Instruct a control to set it's type appropriately + +#define GUI_MSG_INVALIDATE 34 ///< Instruct all controls to refresh - usually due to sizing changes + +#define GUI_MSG_USER 1000 + +/*! + \ingroup winmsg + \brief + */ +#define CONTROL_SELECT(controlID) \ +do { \ + CGUIMessage msg(GUI_MSG_SELECTED, GetID(), controlID); \ + OnMessage(msg); \ +} while(0) + +/*! + \ingroup winmsg + \brief + */ +#define CONTROL_DESELECT(controlID) \ +do { \ + CGUIMessage msg(GUI_MSG_DESELECTED, GetID(), controlID); \ + OnMessage(msg); \ +} while(0) + + +/*! + \ingroup winmsg + \brief + */ +#define CONTROL_ENABLE(controlID) \ +do { \ + CGUIMessage msg(GUI_MSG_ENABLED, GetID(), controlID); \ + OnMessage(msg); \ +} while(0) + +/*! + \ingroup winmsg + \brief + */ +#define CONTROL_DISABLE(controlID) \ +do { \ + CGUIMessage msg(GUI_MSG_DISABLED, GetID(), controlID); \ + OnMessage(msg); \ +} while(0) + + +/*! + \ingroup winmsg + \brief + */ +#define CONTROL_ENABLE_ON_CONDITION(controlID, bCondition) \ +do { \ + CGUIMessage msg(bCondition ? GUI_MSG_ENABLED:GUI_MSG_DISABLED, GetID(), controlID); \ + OnMessage(msg); \ +} while(0) + + +/*! + \ingroup winmsg + \brief + */ +#define CONTROL_SELECT_ITEM(controlID,iItem) \ +do { \ + CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(), controlID,iItem); \ + OnMessage(msg); \ +} while(0) + +/*! + \ingroup winmsg + \brief Set the label of the current control + */ +#define SET_CONTROL_LABEL(controlID,label) \ +do { \ + CGUIMessage msg(GUI_MSG_LABEL_SET, GetID(), controlID); \ + msg.SetLabel(label); \ + OnMessage(msg); \ +} while(0) + +/*! + \ingroup winmsg + \brief Set the second label of the current control + */ +#define SET_CONTROL_LABEL2(controlID,label) \ +do { \ + CGUIMessage msg(GUI_MSG_LABEL2_SET, GetID(), controlID); \ + msg.SetLabel(label); \ + OnMessage(msg); \ +} while(0) + +/*! + \ingroup winmsg + \brief + */ +#define SET_CONTROL_HIDDEN(controlID) \ +do { \ + CGUIMessage msg(GUI_MSG_HIDDEN, GetID(), controlID); \ + OnMessage(msg); \ +} while(0) + +/*! + \ingroup winmsg + \brief + */ +#define SET_CONTROL_FOCUS(controlID, dwParam) \ +do { \ + CGUIMessage msg(GUI_MSG_SETFOCUS, GetID(), controlID, dwParam); \ + OnMessage(msg); \ +} while(0) + +/*! + \ingroup winmsg + \brief + */ +#define SET_CONTROL_VISIBLE(controlID) \ +do { \ + CGUIMessage msg(GUI_MSG_VISIBLE, GetID(), controlID); \ + OnMessage(msg); \ +} while(0) + +#define SET_CONTROL_SELECTED(dwSenderId, controlID, bSelect) \ +do { \ + CGUIMessage msg(bSelect?GUI_MSG_SELECTED:GUI_MSG_DESELECTED, dwSenderId, controlID); \ + OnMessage(msg); \ +} while(0) + +#define BIND_CONTROL(i,c,pv) \ +do { \ + pv = ((c*)GetControl(i));\ +} while(0) + +/*! +\ingroup winmsg +\brief Click message sent from controls to windows. + */ +#define SEND_CLICK_MESSAGE(id, parentID, action) \ +do { \ + CGUIMessage msg(GUI_MSG_CLICKED, id, parentID, action); \ + SendWindowMessage(msg); \ +} while(0) + +#include <vector> +#include "boost/shared_ptr.hpp" +#include "StdString.h" + +// forwards +class CGUIListItem; typedef boost::shared_ptr<CGUIListItem> CGUIListItemPtr; +class CFileItemList; +class CVisualisation; + +/*! + \ingroup winmsg + \brief + */ +class CGUIMessage +{ +public: + CGUIMessage(int dwMsg, int senderID, int controlID, int param1 = 0, int param2 = 0); + CGUIMessage(int msg, int senderID, int controlID, int param1, int param2, CFileItemList* item); + CGUIMessage(int msg, int senderID, int controlID, int param1, int param2, const CGUIListItemPtr &item); + CGUIMessage(int msg, int senderID, int controlID, int param1, int param2, CVisualisation* vis); + CGUIMessage(const CGUIMessage& msg); + virtual ~CGUIMessage(void); + const CGUIMessage& operator = (const CGUIMessage& msg); + + int GetControlId() const ; + int GetMessage() const; + void* GetPointer() const; + CGUIListItemPtr GetItem() const; + int GetParam1() const; + int GetParam2() const; + int GetSenderId() const; + void SetParam1(int param1); + void SetParam2(int param2); + void SetPointer(void* pointer); + void SetLabel(const std::string& strLabel); + void SetLabel(int iString); // for convience - looks up in strings.xml + const std::string& GetLabel() const; + void SetStringParam(const CStdString &strParam); + void SetStringParams(const std::vector<CStdString> ¶ms); + const CStdString& GetStringParam(size_t param = 0) const; + size_t GetNumStringParams() const; + void SetAction(const CGUIActionDescriptor& action); + const CGUIActionDescriptor& GetAction() const; + +private: + std::string m_strLabel; + std::vector<CStdString> m_params; + CGUIActionDescriptor m_action; + int m_senderID; + int m_controlID; + int m_message; + void* m_pointer; + int m_param1; + int m_param2; + CGUIListItemPtr m_item; + + static CStdString empty_string; +}; +#endif diff --git a/guilib/GUIMoverControl.cpp b/guilib/GUIMoverControl.cpp new file mode 100644 index 0000000000..098e890d8f --- /dev/null +++ b/guilib/GUIMoverControl.cpp @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIMoverControl.h" +#include "GUIWindowManager.h" +#include "MouseStat.h" +#include "Key.h" + +// time to reset accelerated cursors (digital movement) +#define MOVE_TIME_OUT 500L + +CGUIMoverControl::CGUIMoverControl(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& textureFocus, const CTextureInfo& textureNoFocus) + : CGUIControl(parentID, controlID, posX, posY, width, height) + , m_imgFocus(posX, posY, width, height, textureFocus) + , m_imgNoFocus(posX, posY, width, height, textureNoFocus) +{ + m_dwFrameCounter = 0; + m_dwLastMoveTime = 0; + m_fSpeed = 1.0; + m_fAnalogSpeed = 2.0f; // TODO: implement correct analog speed + m_fAcceleration = 0.2f; // TODO: implement correct computation of acceleration + m_fMaxSpeed = 10.0; // TODO: implement correct computation of maxspeed + ControlType = GUICONTROL_MOVER; + SetLimits(0, 0, 720, 576); // defaults + SetLocation(0, 0, false); // defaults +} + +CGUIMoverControl::~CGUIMoverControl(void) +{} + +void CGUIMoverControl::Render() +{ + if (m_bInvalidated) + { + m_imgFocus.SetWidth(m_width); + m_imgFocus.SetHeight(m_height); + + m_imgNoFocus.SetWidth(m_width); + m_imgNoFocus.SetHeight(m_height); + } + if (HasFocus()) + { + DWORD dwAlphaCounter = m_dwFrameCounter + 2; + DWORD dwAlphaChannel; + if ((dwAlphaCounter % 128) >= 64) + dwAlphaChannel = dwAlphaCounter % 64; + else + dwAlphaChannel = 63 - (dwAlphaCounter % 64); + + dwAlphaChannel += 192; + SetAlpha( (unsigned char)dwAlphaChannel ); + m_imgFocus.SetVisible(true); + m_imgNoFocus.SetVisible(false); + m_dwFrameCounter++; + } + else + { + SetAlpha(0xff); + m_imgFocus.SetVisible(false); + m_imgNoFocus.SetVisible(true); + } + // render both so the visibility settings cause the frame counter to resetcorrectly + m_imgFocus.Render(); + m_imgNoFocus.Render(); + CGUIControl::Render(); +} + +bool CGUIMoverControl::OnAction(const CAction &action) +{ + if (action.id == ACTION_SELECT_ITEM) + { + // button selected - send message to parent + CGUIMessage message(GUI_MSG_CLICKED, GetID(), GetParentID()); + SendWindowMessage(message); + return true; + } + if (action.id == ACTION_ANALOG_MOVE) + { + // if (m_dwAllowedDirections == ALLOWED_DIRECTIONS_UPDOWN) + // Move(0, (int)(-m_fAnalogSpeed*action.amount2)); + // else if (m_dwAllowedDirections == ALLOWED_DIRECTIONS_LEFTRIGHT) + // Move((int)(m_fAnalogSpeed*action.amount1), 0); + // else // ALLOWED_DIRECTIONS_ALL + Move((int)(m_fAnalogSpeed*action.amount1), (int)( -m_fAnalogSpeed*action.amount2)); + return true; + } + // base class + return CGUIControl::OnAction(action); +} + +void CGUIMoverControl::OnUp() +{ + // if (m_dwAllowedDirections == ALLOWED_DIRECTIONS_LEFTRIGHT) return; + UpdateSpeed(DIRECTION_UP); + Move(0, (int) - m_fSpeed); +} + +void CGUIMoverControl::OnDown() +{ + // if (m_dwAllowedDirections == ALLOWED_DIRECTIONS_LEFTRIGHT) return; + UpdateSpeed(DIRECTION_DOWN); + Move(0, (int)m_fSpeed); +} + +void CGUIMoverControl::OnLeft() +{ + // if (m_dwAllowedDirections == ALLOWED_DIRECTIONS_UPDOWN) return; + UpdateSpeed(DIRECTION_LEFT); + Move((int) - m_fSpeed, 0); +} + +void CGUIMoverControl::OnRight() +{ + // if (m_dwAllowedDirections == ALLOWED_DIRECTIONS_UPDOWN) return; + UpdateSpeed(DIRECTION_RIGHT); + Move((int)m_fSpeed, 0); +} + +bool CGUIMoverControl::OnMouseDrag(const CPoint &offset, const CPoint &point) +{ + g_Mouse.SetState(MOUSE_STATE_DRAG); + g_Mouse.SetExclusiveAccess(GetID(), GetParentID(), point); + Move((int)offset.x, (int)offset.y); + return true; +} + +bool CGUIMoverControl::OnMouseClick(int button, const CPoint &point) +{ + if (button != MOUSE_LEFT_BUTTON) return false; + g_Mouse.EndExclusiveAccess(GetID(), GetParentID()); + return true; +} + +void CGUIMoverControl::UpdateSpeed(int nDirection) +{ + if (timeGetTime() - m_dwLastMoveTime > MOVE_TIME_OUT) + { + m_fSpeed = 1; + m_nDirection = DIRECTION_NONE; + } + m_dwLastMoveTime = timeGetTime(); + if (nDirection == m_nDirection) + { // accelerate + m_fSpeed += m_fAcceleration; + if (m_fSpeed > m_fMaxSpeed) m_fSpeed = m_fMaxSpeed; + } + else + { // reset direction and speed + m_fSpeed = 1; + m_nDirection = nDirection; + } +} + +void CGUIMoverControl::AllocResources() +{ + CGUIControl::AllocResources(); + m_dwFrameCounter = 0; + m_imgFocus.AllocResources(); + m_imgNoFocus.AllocResources(); + float width = m_width ? m_width : m_imgFocus.GetWidth(); + float height = m_height ? m_height : m_imgFocus.GetHeight(); + SetWidth(width); + SetHeight(height); +} + +void CGUIMoverControl::FreeResources() +{ + CGUIControl::FreeResources(); + m_imgFocus.FreeResources(); + m_imgNoFocus.FreeResources(); +} + +void CGUIMoverControl::DynamicResourceAlloc(bool bOnOff) +{ + CGUIControl::DynamicResourceAlloc(bOnOff); + m_imgFocus.DynamicResourceAlloc(bOnOff); + m_imgNoFocus.DynamicResourceAlloc(bOnOff); +} + +void CGUIMoverControl::Move(int iX, int iY) +{ + int iLocX = m_iLocationX + iX; + int iLocY = m_iLocationY + iY; + // check if we are within the bounds + if (iLocX < m_iX1) iLocX = m_iX1; + if (iLocY < m_iY1) iLocY = m_iY1; + if (iLocX > m_iX2) iLocX = m_iX2; + if (iLocY > m_iY2) iLocY = m_iY2; + // ok, now set the location of the mover + SetLocation(iLocX, iLocY); +} + +void CGUIMoverControl::SetLocation(int iLocX, int iLocY, bool bSetPosition) +{ + if (bSetPosition) SetPosition(GetXPosition() + iLocX - m_iLocationX, GetYPosition() + iLocY - m_iLocationY); + m_iLocationX = iLocX; + m_iLocationY = iLocY; +} + +void CGUIMoverControl::SetPosition(float posX, float posY) +{ + CGUIControl::SetPosition(posX, posY); + m_imgFocus.SetPosition(posX, posY); + m_imgNoFocus.SetPosition(posX, posY); +} + +void CGUIMoverControl::SetAlpha(unsigned char alpha) +{ + m_imgFocus.SetAlpha(alpha); + m_imgNoFocus.SetAlpha(alpha); +} + +void CGUIMoverControl::UpdateColors() +{ + CGUIControl::UpdateColors(); + m_imgFocus.SetDiffuseColor(m_diffuseColor); + m_imgNoFocus.SetDiffuseColor(m_diffuseColor); +} + +void CGUIMoverControl::SetLimits(int iX1, int iY1, int iX2, int iY2) +{ + m_iX1 = iX1; + m_iY1 = iY1; + m_iX2 = iX2; + m_iY2 = iY2; +} diff --git a/guilib/GUIMoverControl.h b/guilib/GUIMoverControl.h new file mode 100644 index 0000000000..5cda5a0590 --- /dev/null +++ b/guilib/GUIMoverControl.h @@ -0,0 +1,97 @@ +/*! +\file GUIMoverControl.h +\brief +*/ + +#ifndef GUILIB_GUIMoverCONTROL_H +#define GUILIB_GUIMoverCONTROL_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUITexture.h" +#include "GUIControl.h" + +#define ALLOWED_DIRECTIONS_ALL 0 +#define ALLOWED_DIRECTIONS_UPDOWN 1 +#define ALLOWED_DIRECTIONS_LEFTRIGHT 2 + +#define DIRECTION_NONE 0 +#define DIRECTION_UP 1 +#define DIRECTION_DOWN 2 +#define DIRECTION_LEFT 3 +#define DIRECTION_RIGHT 4 + +// normal alignment is TOP LEFT 0 = topleft, 1 = topright +#define ALIGN_RIGHT 1 +#define ALIGN_BOTTOM 2 + +/*! + \ingroup controls + \brief + */ +class CGUIMoverControl : public CGUIControl +{ +public: + CGUIMoverControl(int parentID, int controlID, + float posX, float posY, float width, float height, + const CTextureInfo& textureFocus, const CTextureInfo& textureNoFocus); + + virtual ~CGUIMoverControl(void); + virtual CGUIMoverControl *Clone() const { return new CGUIMoverControl(*this); }; + + virtual void Render(); + virtual bool OnAction(const CAction &action); + virtual void OnUp(); + virtual void OnDown(); + virtual void OnLeft(); + virtual void OnRight(); + virtual bool OnMouseDrag(const CPoint &offset, const CPoint &point); + virtual bool OnMouseClick(int button, const CPoint &point); + virtual void AllocResources(); + virtual void FreeResources(); + virtual void DynamicResourceAlloc(bool bOnOff); + virtual void SetPosition(float posX, float posY); + void SetLimits(int iX1, int iY1, int iX2, int iY2); + void SetLocation(int iLocX, int iLocY, bool bSetPosition = true); + int GetXLocation() const { return m_iLocationX;}; + int GetYLocation() const { return m_iLocationY;}; + +protected: + virtual void UpdateColors(); + void SetAlpha(unsigned char alpha); + void UpdateSpeed(int nDirection); + void Move(int iX, int iY); + CGUITexture m_imgFocus; + CGUITexture m_imgNoFocus; + DWORD m_dwFrameCounter; + DWORD m_dwLastMoveTime; + int m_nDirection; + float m_fSpeed; + float m_fAnalogSpeed; + float m_fMaxSpeed; + float m_fAcceleration; + int m_iX1, m_iX2, m_iY1, m_iY2; + int m_iLocationX, m_iLocationY; +}; +#endif diff --git a/guilib/GUIMultiImage.cpp b/guilib/GUIMultiImage.cpp new file mode 100644 index 0000000000..75db87e921 --- /dev/null +++ b/guilib/GUIMultiImage.cpp @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIMultiImage.h" +#include "TextureManager.h" +#include "FileSystem/Directory.h" +#include "Util.h" +#include "FileItem.h" +#include "Key.h" + +using namespace std; +using namespace DIRECTORY; + +CGUIMultiImage::CGUIMultiImage(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& texture, DWORD timePerImage, DWORD fadeTime, bool randomized, bool loop, DWORD timeToPauseAtEnd) + : CGUIControl(parentID, controlID, posX, posY, width, height), + m_image(0, 0, posX, posY, width, height, texture) +{ + m_currentImage = 0; + m_timePerImage = timePerImage + fadeTime; + m_timeToPauseAtEnd = timeToPauseAtEnd; + m_image.SetCrossFade(fadeTime); + m_randomized = randomized; + m_loop = loop; + ControlType = GUICONTROL_MULTI_IMAGE; + m_bDynamicResourceAlloc=false; + m_directoryLoaded = false; +} + +CGUIMultiImage::CGUIMultiImage(const CGUIMultiImage &from) +: CGUIControl(from), m_image(from.m_image) +{ + m_texturePath = from.m_texturePath; + m_timePerImage = from.m_timePerImage; + m_timeToPauseAtEnd = from.m_timeToPauseAtEnd; + m_randomized = from.m_randomized; + m_loop = from.m_loop; + m_bDynamicResourceAlloc=false; + m_directoryLoaded = false; + if (m_texturePath.IsConstant()) + m_currentPath = m_texturePath.GetLabel(WINDOW_INVALID); + m_currentImage = 0; + ControlType = GUICONTROL_MULTI_IMAGE; +} + +CGUIMultiImage::~CGUIMultiImage(void) +{ +} + +void CGUIMultiImage::UpdateVisibility(const CGUIListItem *item) +{ + CGUIControl::UpdateVisibility(item); + + // check if we're hidden, and deallocate if so + if (!IsVisible() && m_visible != DELAYED) + { + if (m_bDynamicResourceAlloc && m_bAllocated) + FreeResources(); + return; + } + + // we are either delayed or visible, so we can allocate our resources + if (!m_directoryLoaded) + { + LoadDirectory(); + m_image.SetFileName(m_files.size() ? m_files[0] : ""); + } + if (!m_bAllocated) + AllocResources(); +} + +void CGUIMultiImage::UpdateInfo(const CGUIListItem *item) +{ + // check for conditional information before we + // alloc as this can free our resources + if (!m_texturePath.IsConstant()) + { + CStdString texturePath(m_texturePath.GetLabel(m_parentID)); + if (texturePath != m_currentPath && !texturePath.IsEmpty()) + { + // a new path - set our current path and tell ourselves to load our directory + m_currentPath = texturePath; + m_directoryLoaded = false; + } + } +} + +void CGUIMultiImage::Render() +{ + if (!m_files.empty()) + { + // Set a viewport so that we don't render outside the defined area + g_graphicsContext.SetClipRegion(m_posX, m_posY, m_width, m_height); + + unsigned int nextImage = m_currentImage + 1; + if (nextImage >= m_files.size()) + nextImage = m_loop ? 0 : m_currentImage; // stay on the last image if <loop>no</loop> + + if (nextImage != m_currentImage) + { + // check if we should be loading a new image yet + DWORD timeToShow = m_timePerImage; + if (0 == nextImage) // last image should be paused for a bit longer if that's what the skinner wishes. + timeToShow += m_timeToPauseAtEnd; + if (m_imageTimer.IsRunning() && m_imageTimer.GetElapsedMilliseconds() > timeToShow) + { + // grab a new image + m_currentImage = nextImage; + m_image.SetFileName(m_files[m_currentImage]); + m_imageTimer.StartZero(); + } + } + m_image.SetColorDiffuse(m_diffuseColor); + m_image.Render(); + g_graphicsContext.RestoreClipRegion(); + } + CGUIControl::Render(); +} + +bool CGUIMultiImage::OnAction(const CAction &action) +{ + return false; +} + +bool CGUIMultiImage::OnMessage(CGUIMessage &message) +{ + if (message.GetMessage() == GUI_MSG_REFRESH_THUMBS) + { + if (!m_texturePath.IsConstant()) + FreeResources(); + return true; + } + return CGUIControl::OnMessage(message); +} + +void CGUIMultiImage::AllocResources() +{ + FreeResources(); + CGUIControl::AllocResources(); + + if (!m_directoryLoaded) + LoadDirectory(); + + // Load in the current image, and reset our timer + m_currentImage = 0; + m_imageTimer.StartZero(); + + // and re-randomize if our control has been reallocated + if (m_randomized) + random_shuffle(m_files.begin(), m_files.end()); + + m_image.SetFileName(m_files.size() ? m_files[0] : ""); +} + +void CGUIMultiImage::FreeResources() +{ + m_image.FreeResources(); + m_currentImage = 0; + CGUIControl::FreeResources(); +} + +void CGUIMultiImage::DynamicResourceAlloc(bool bOnOff) +{ + CGUIControl::DynamicResourceAlloc(bOnOff); + m_bDynamicResourceAlloc=bOnOff; +} + +bool CGUIMultiImage::CanFocus() const +{ + return false; +} + +void CGUIMultiImage::SetAspectRatio(const CAspectRatio &ratio) +{ + m_image.SetAspectRatio(ratio); +} + +void CGUIMultiImage::LoadDirectory() +{ + // Load any images from our texture bundle first + m_files.clear(); + + // don't load any images if our path is empty + if (m_currentPath.IsEmpty()) return; + + // check to see if we have a single image or a folder of images + CFileItem item(m_currentPath, false); + if (item.IsPicture()) + { + m_files.push_back(m_currentPath); + } + else + { // folder of images + g_TextureManager.GetBundledTexturesFromPath(m_currentPath, m_files); + + // Load in our images from the directory specified + // m_currentPath is relative (as are all skin paths) + CStdString realPath = g_TextureManager.GetTexturePath(m_currentPath, true); + if (realPath.IsEmpty()) + return; + + CUtil::AddSlashAtEnd(realPath); + CFileItemList items; + CDirectory::GetDirectory(realPath, items); + for (int i=0; i < items.Size(); i++) + { + CFileItemPtr pItem = items[i]; + if (pItem->IsPicture()) + m_files.push_back(pItem->m_strPath); + } + } + + // Randomize or sort our images if necessary + if (m_randomized) + random_shuffle(m_files.begin(), m_files.end()); + else + sort(m_files.begin(), m_files.end()); + + // flag as loaded - no point in constantly reloading them + m_directoryLoaded = true; + m_imageTimer.StartZero(); +} + +void CGUIMultiImage::SetInfo(const CGUIInfoLabel &info) +{ + m_texturePath = info; + if (m_texturePath.IsConstant()) + m_currentPath = m_texturePath.GetLabel(WINDOW_INVALID); +} diff --git a/guilib/GUIMultiImage.h b/guilib/GUIMultiImage.h new file mode 100644 index 0000000000..60b03d966b --- /dev/null +++ b/guilib/GUIMultiImage.h @@ -0,0 +1,79 @@ +/*! +\file GUIMultiImage.h +\brief +*/ + +#ifndef GUILIB_GUIMULTIIMAGECONTROL_H +#define GUILIB_GUIMULTIIMAGECONTROL_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIImage.h" +#include "utils/Stopwatch.h" + +/*! + \ingroup controls + \brief + */ +class CGUIMultiImage : public CGUIControl +{ +public: + CGUIMultiImage(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& texture, DWORD timePerImage, DWORD fadeTime, bool randomized, bool loop, DWORD timeToPauseAtEnd); + CGUIMultiImage(const CGUIMultiImage &from); + virtual ~CGUIMultiImage(void); + virtual CGUIMultiImage *Clone() const { return new CGUIMultiImage(*this); }; + + virtual void Render(); + virtual void UpdateVisibility(const CGUIListItem *item = NULL); + virtual void UpdateInfo(const CGUIListItem *item = NULL); + virtual bool OnAction(const CAction &action); + virtual bool OnMessage(CGUIMessage &message); + virtual void AllocResources(); + virtual void FreeResources(); + virtual void DynamicResourceAlloc(bool bOnOff); + virtual bool IsDynamicallyAllocated() { return m_bDynamicResourceAlloc; }; + virtual bool CanFocus() const; + + void SetInfo(const CGUIInfoLabel &info); + void SetAspectRatio(const CAspectRatio &ratio); + +protected: + void LoadDirectory(); + + CGUIInfoLabel m_texturePath; + CStdString m_currentPath; + unsigned int m_currentImage; + CStopWatch m_imageTimer; + DWORD m_timePerImage; + DWORD m_timeToPauseAtEnd; + bool m_randomized; + bool m_loop; + + bool m_bDynamicResourceAlloc; + bool m_directoryLoaded; + std::vector<CStdString> m_files; + + CGUIImage m_image; +}; +#endif diff --git a/guilib/GUIMultiSelectText.cpp b/guilib/GUIMultiSelectText.cpp new file mode 100644 index 0000000000..c2d5d4f9f4 --- /dev/null +++ b/guilib/GUIMultiSelectText.cpp @@ -0,0 +1,439 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIMultiSelectText.h" +#include "Key.h" +#include "MouseStat.h" +#include "utils/log.h" + +using namespace std; + +CGUIMultiSelectTextControl::CSelectableString::CSelectableString(CGUIFont *font, const CStdString &text, bool selectable, const CStdString &clickAction) + : m_text(font, false) +{ + m_selectable = selectable; + m_clickAction = clickAction; + m_clickAction.TrimLeft(" ="); + m_clickAction.TrimRight(" "); + m_text.Update(text); + float height; + m_text.GetTextExtent(m_length, height); +} + +CGUIMultiSelectTextControl::CGUIMultiSelectTextControl(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& textureFocus, const CTextureInfo& textureNoFocus, const CLabelInfo& labelInfo, const CGUIInfoLabel &content) + : CGUIControl(parentID, controlID, posX, posY, width, height) + , m_button(parentID, controlID, posX, posY, width, height, textureFocus, textureNoFocus, labelInfo) +{ + m_info = content; + m_label = labelInfo; + m_selectedItem = 0; + m_offset = 0; + m_totalWidth = 0; + m_scrollOffset = 0; + m_scrollSpeed = 0; + m_scrollLastTime = 0; + m_renderTime = 0; + m_label.align &= ~3; // we currently ignore all x alignment +} + +CGUIMultiSelectTextControl::~CGUIMultiSelectTextControl(void) +{ +} + +void CGUIMultiSelectTextControl::DoRender(DWORD currentTime) +{ + m_renderTime = currentTime; + CGUIControl::DoRender(currentTime); +} + +void CGUIMultiSelectTextControl::UpdateColors() +{ + m_label.UpdateColors(); + CGUIControl::UpdateColors(); +} + +void CGUIMultiSelectTextControl::Render() +{ + // check our selected item is in range + unsigned int numSelectable = GetNumSelectable(); + if (!numSelectable) + SetFocus(false); + else if (m_selectedItem >= numSelectable) + m_selectedItem = numSelectable - 1; + + // and validate our offset + if (m_offset + m_width > m_totalWidth) + m_offset = m_totalWidth - m_width; + if (m_offset < 0) m_offset = 0; + + // handle scrolling + m_scrollOffset += m_scrollSpeed * (m_renderTime - m_scrollLastTime); + if ((m_scrollSpeed < 0 && m_scrollOffset < m_offset) || + (m_scrollSpeed > 0 && m_scrollOffset > m_offset)) + { + m_scrollOffset = m_offset; + m_scrollSpeed = 0; + } + m_scrollLastTime = m_renderTime; + + // clip and set our scrolling origin + bool clip(m_width < m_totalWidth); + if (clip) + { // need to crop + if (!g_graphicsContext.SetClipRegion(m_posX, m_posY, m_width, m_height)) + return; // nothing to render?? + } + g_graphicsContext.SetOrigin(-m_scrollOffset, 0); + + // render the buttons + for (unsigned int i = 0; i < m_buttons.size(); i++) + { + m_buttons[i].SetFocus(HasFocus() && i == m_selectedItem); + m_buttons[i].DoRender(m_renderTime); + } + + // position the text - we center vertically if applicable, and use the offsets. + // all x-alignment is ignored for now (see constructor) + float posX = m_posX; + float posY = m_posY + m_label.offsetY; + if (m_label.align & XBFONT_CENTER_Y) + posY = m_posY + m_height * 0.5f; + + if (m_items.size() && m_items[0].m_selectable) + posX += m_label.offsetX; + + // render the text + unsigned int num_selectable = 0; + for (unsigned int i = 0; i < m_items.size(); i++) + { + CSelectableString &string = m_items[i]; + if (IsDisabled()) // all text is rendered with disabled color + string.m_text.Render(posX, posY, 0, m_label.disabledColor, m_label.shadowColor, m_label.align, 0, true); + else if (HasFocus() && string.m_selectable && num_selectable == m_selectedItem) // text is rendered with focusedcolor + string.m_text.Render(posX, posY, 0, m_label.focusedColor, m_label.shadowColor, m_label.align, 0); + else // text is rendered with textcolor + string.m_text.Render(posX, posY, 0, m_label.textColor, m_label.shadowColor, m_label.align, 0); + posX += string.m_length; + if (string.m_selectable) + num_selectable++; + } + + g_graphicsContext.RestoreOrigin(); + if (clip) + g_graphicsContext.RestoreClipRegion(); + + CGUIControl::Render(); +} + +void CGUIMultiSelectTextControl::UpdateInfo(const CGUIListItem *item) +{ + if (m_info.IsEmpty()) + return; // nothing to do + + if (item) + UpdateText(m_info.GetItemLabel(item)); + else + UpdateText(m_info.GetLabel(m_parentID)); +} + +bool CGUIMultiSelectTextControl::OnAction(const CAction &action) +{ + if (action.id == ACTION_SELECT_ITEM) + { + // item is clicked - see if we have a clickaction + CStdString clickAction; + unsigned int selected = 0; + for (unsigned int i = 0; i < m_items.size(); i++) + { + if (m_items[i].m_selectable) + { + if (m_selectedItem == selected) + clickAction = m_items[i].m_clickAction; + selected++; + } + } + if (!clickAction.IsEmpty()) + { // have a click action -> perform it + CGUIMessage message(GUI_MSG_EXECUTE, m_controlID, m_parentID); + message.SetStringParam(clickAction); + g_graphicsContext.SendMessage(message); + } + else + { // no click action, just send a message to the window + CGUIMessage msg(GUI_MSG_CLICKED, m_controlID, m_parentID, m_selectedItem); + SendWindowMessage(msg); + } + return true; + } + return CGUIControl::OnAction(action); +} + +void CGUIMultiSelectTextControl::OnLeft() +{ + if (MoveLeft()) + return; + CGUIControl::OnLeft(); +} + +void CGUIMultiSelectTextControl::OnRight() +{ + if (MoveRight()) + return; + CGUIControl::OnRight(); +} + +// movement functions (callable from lists) +bool CGUIMultiSelectTextControl::MoveLeft() +{ + if (m_selectedItem > 0) + ScrollToItem(m_selectedItem - 1); + else if (GetNumSelectable() && m_controlLeft && m_controlLeft == m_controlID) + ScrollToItem(GetNumSelectable() - 1); + else + return false; + return true; +} + +bool CGUIMultiSelectTextControl::MoveRight() +{ + if (GetNumSelectable() && m_selectedItem < GetNumSelectable() - 1) + ScrollToItem(m_selectedItem + 1); + else if (m_controlRight && m_controlRight == m_controlID) + ScrollToItem(0); + else + return false; + return true; +} + +void CGUIMultiSelectTextControl::SelectItemFromPoint(const CPoint &point) +{ + int item = GetItemFromPoint(point); + if (item != -1) + { + ScrollToItem(item); + SetFocus(true); + } + else + SetFocus(false); +} + +bool CGUIMultiSelectTextControl::HitTest(const CPoint &point) const +{ + return (GetItemFromPoint(point) != -1); +} + +bool CGUIMultiSelectTextControl::OnMouseOver(const CPoint &point) +{ + ScrollToItem(GetItemFromPoint(point)); + return CGUIControl::OnMouseOver(point); +} + +bool CGUIMultiSelectTextControl::OnMouseClick(int button, const CPoint &point) +{ + if (button == MOUSE_LEFT_BUTTON) + { + m_selectedItem = GetItemFromPoint(point); + g_Mouse.SetState(MOUSE_STATE_CLICK); + CAction action; + action.id = ACTION_SELECT_ITEM; + OnAction(action); + return true; + } + return false; +} + +int CGUIMultiSelectTextControl::GetItemFromPoint(const CPoint &point) const +{ + if (!m_label.font) return -1; + float posX = m_posX; + unsigned int selectable = 0; + for (unsigned int i = 0; i < m_items.size(); i++) + { + const CSelectableString &string = m_items[i]; + if (string.m_selectable) + { + CRect rect(posX, m_posY, posX + string.m_length, m_posY + m_height); + if (rect.PtInRect(point)) + return selectable; + selectable++; + } + posX += string.m_length; + } + return -1; +} + +void CGUIMultiSelectTextControl::UpdateText(const CStdString &text) +{ + if (text == m_oldText) + return; + + m_items.clear(); + + // parse our text into clickable blocks + // format is [ONCLICK <action>] [/ONCLICK] + size_t startClickable = text.Find("[ONCLICK"); + size_t startUnclickable = 0; + + // add the first unclickable block + if (startClickable != CStdString::npos) + AddString(text.Mid(startUnclickable, startClickable - startUnclickable), false); + else + AddString(text.Mid(startUnclickable), false); + while (startClickable != CStdString::npos) + { + // grep out the action and the end of the string + size_t endAction = text.Find(']', startClickable + 8); + size_t endClickable = text.Find("[/ONCLICK]", startClickable + 8); + if (endAction != CStdString::npos && endClickable != CStdString::npos) + { // success - add the string, and move the start of our next unclickable portion along + AddString(text.Mid(endAction + 1, endClickable - endAction - 1), true, text.Mid(startClickable + 8, endAction - startClickable - 8)); + startUnclickable = endClickable + 10; + } + else + { + CLog::Log(LOGERROR, "Invalid multiselect string %s", text.c_str()); + break; + } + startClickable = text.Find("[ONCLICK", startUnclickable); + // add the unclickable portion + if (startClickable != CStdString::npos) + AddString(text.Mid(startUnclickable, startClickable - startUnclickable), false); + else + AddString(text.Mid(startUnclickable), false); + } + + m_oldText = text; + + // finally, position our buttons + PositionButtons(); +} + +void CGUIMultiSelectTextControl::AddString(const CStdString &text, bool selectable, const CStdString &clickAction) +{ + if (!text.IsEmpty()) + m_items.push_back(CSelectableString(m_label.font, text, selectable, clickAction)); +} + +void CGUIMultiSelectTextControl::PositionButtons() +{ + m_buttons.clear(); + + // add new buttons + m_totalWidth = 0; + if (m_items.size() && m_items.front().m_selectable) + m_totalWidth += m_label.offsetX; + + for (unsigned int i = 0; i < m_items.size(); i++) + { + const CSelectableString &text = m_items[i]; + if (text.m_selectable) + { + CGUIButtonControl button(m_button); + button.SetPosition(m_posX + m_totalWidth - m_label.offsetX, m_posY); + button.SetWidth(text.m_length + 2 * m_label.offsetX); + m_buttons.push_back(button); + } + m_totalWidth += text.m_length; + } + + if (m_items.size() && m_items.back().m_selectable) + m_totalWidth += m_label.offsetX; +} + +CStdString CGUIMultiSelectTextControl::GetDescription() const +{ + // We currently just return the entire string - should we bother returning the + // particular subitems of this? + CStdString strLabel(m_info.GetLabel(m_parentID)); + return strLabel; +} + +unsigned int CGUIMultiSelectTextControl::GetNumSelectable() const +{ + unsigned int selectable = 0; + for (unsigned int i = 0; i < m_items.size(); i++) + if (m_items[i].m_selectable) + selectable++; + return selectable; +} + +unsigned int CGUIMultiSelectTextControl::GetFocusedItem() const +{ + if (GetNumSelectable()) + return m_selectedItem + 1; + return 0; +} + +void CGUIMultiSelectTextControl::SetFocusedItem(unsigned int item) +{ + SetFocus(item > 0); + if (item > 0) + ScrollToItem(item - 1); +} + +bool CGUIMultiSelectTextControl::CanFocus() const +{ + if (!GetNumSelectable()) return false; + return CGUIControl::CanFocus(); +} + +void CGUIMultiSelectTextControl::SetFocus(bool focus) +{ + for (unsigned int i = 0; i < m_buttons.size(); i++) + m_buttons[i].SetFocus(focus); + CGUIControl::SetFocus(focus); +} + +// overrides to allow anims to translate down to the focus image +void CGUIMultiSelectTextControl::SetAnimations(const vector<CAnimation> &animations) +{ + // send any focus animations down to the focus image only + m_animations.clear(); + vector<CAnimation> focusAnims; + for (unsigned int i = 0; i < animations.size(); i++) + { + const CAnimation &anim = animations[i]; + if (anim.GetType() == ANIM_TYPE_FOCUS) + focusAnims.push_back(anim); + else + m_animations.push_back(anim); + } + m_button.SetAnimations(focusAnims); +} + +void CGUIMultiSelectTextControl::ScrollToItem(unsigned int item) +{ + static const unsigned int time_to_scroll = 200; + if (item >= m_buttons.size()) return; + // grab our button + const CGUIButtonControl &button = m_buttons[item]; + float left = button.GetXPosition(); + float right = left + button.GetWidth(); + // make sure that we scroll so that this item is on screen + m_scrollOffset = m_offset; + if (left < m_posX + m_offset) + m_offset = left - m_posX; + else if (right > m_posX + m_offset + m_width) + m_offset = right - m_width - m_posX; + m_scrollSpeed = (m_offset - m_scrollOffset) / time_to_scroll; + m_selectedItem = item; +} + diff --git a/guilib/GUIMultiSelectText.h b/guilib/GUIMultiSelectText.h new file mode 100644 index 0000000000..56e3d7a905 --- /dev/null +++ b/guilib/GUIMultiSelectText.h @@ -0,0 +1,102 @@ +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUILabelControl.h" // for CInfoPortion +#include "GUIButtonControl.h" + +/*! + \ingroup controls + \brief + */ +class CGUIMultiSelectTextControl : public CGUIControl +{ +public: + CGUIMultiSelectTextControl(int parentID, int controlID, + float posX, float posY, float width, float height, + const CTextureInfo& textureFocus, const CTextureInfo& textureNoFocus, const CLabelInfo &label, const CGUIInfoLabel &content); + + virtual ~CGUIMultiSelectTextControl(void); + virtual CGUIMultiSelectTextControl *Clone() const { return new CGUIMultiSelectTextControl(*this); }; + + virtual void DoRender(DWORD currentTime); + virtual void Render(); + + virtual bool OnAction(const CAction &action); + virtual void OnLeft(); + virtual void OnRight(); + virtual bool HitTest(const CPoint &point) const; + virtual bool OnMouseOver(const CPoint &point); + virtual bool OnMouseClick(int button, const CPoint &point); + virtual void UpdateInfo(const CGUIListItem *item = NULL); + + virtual CStdString GetDescription() const; + virtual bool CanFocus() const; + + void UpdateText(const CStdString &text); + bool MoveLeft(); + bool MoveRight(); + void SelectItemFromPoint(const CPoint &point); + unsigned int GetFocusedItem() const; + void SetFocusedItem(unsigned int item); + + // overrides to allow all focus anims to translate down to the focus image + virtual void SetAnimations(const std::vector<CAnimation> &animations); + virtual void SetFocus(bool focus); +protected: + virtual void UpdateColors(); + void AddString(const CStdString &text, bool selectable, const CStdString &clickAction = ""); + void PositionButtons(); + unsigned int GetNumSelectable() const; + int GetItemFromPoint(const CPoint &point) const; + void ScrollToItem(unsigned int item); + + // the static strings and buttons strings + class CSelectableString + { + public: + CSelectableString(CGUIFont *font, const CStdString &text, bool selectable, const CStdString &clickAction); + CGUITextLayout m_text; + float m_length; + bool m_selectable; + CStdString m_clickAction; + }; + std::vector<CSelectableString> m_items; + + CLabelInfo m_label; + CGUIInfoLabel m_info; + CStdString m_oldText; + DWORD m_renderTime; + + // scrolling + float m_totalWidth; + float m_offset; + float m_scrollOffset; + float m_scrollSpeed; + DWORD m_scrollLastTime; + + // buttons + CGUIButtonControl m_button; + unsigned int m_selectedItem; + std::vector<CGUIButtonControl> m_buttons; +}; + diff --git a/guilib/GUIPanelContainer.cpp b/guilib/GUIPanelContainer.cpp new file mode 100644 index 0000000000..6d5b12d5a0 --- /dev/null +++ b/guilib/GUIPanelContainer.cpp @@ -0,0 +1,482 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIPanelContainer.h" +#include "GUIListItem.h" +#include "utils/GUIInfoManager.h" +#include "Key.h" + +using namespace std; + +CGUIPanelContainer::CGUIPanelContainer(int parentID, int controlID, float posX, float posY, float width, float height, ORIENTATION orientation, int scrollTime, int preloadItems) + : CGUIBaseContainer(parentID, controlID, posX, posY, width, height, orientation, scrollTime, preloadItems) +{ + ControlType = GUICONTAINER_PANEL; + m_type = VIEW_TYPE_ICON; + m_itemsPerRow = 1; +} + +CGUIPanelContainer::~CGUIPanelContainer(void) +{ +} + +void CGUIPanelContainer::Render() +{ + ValidateOffset(); + + if (m_bInvalidated) + UpdateLayout(); + + if (!m_layout || !m_focusedLayout) return; + + UpdateScrollOffset(); + + int offset = (int)(m_scrollOffset / m_layout->Size(m_orientation)); + + int cacheBefore, cacheAfter; + GetCacheOffsets(cacheBefore, cacheAfter); + + // Free memory not used on screen at the moment, do this first so there's more memory for the new items. + FreeMemory(CorrectOffset(offset - cacheBefore, 0), CorrectOffset(offset + cacheAfter + m_itemsPerPage + 1, 0)); + + g_graphicsContext.SetClipRegion(m_posX, m_posY, m_width, m_height); + float pos = (m_orientation == VERTICAL) ? m_posY : m_posX; + float end = (m_orientation == VERTICAL) ? m_posY + m_height : m_posX + m_width; + pos += (offset - cacheBefore) * m_layout->Size(m_orientation) - m_scrollOffset; + end += cacheAfter * m_layout->Size(m_orientation); + + float focusedPos = 0; + int focusedCol = 0; + CGUIListItemPtr focusedItem; + int current = (offset - cacheBefore) * m_itemsPerRow; + int col = 0; + while (pos < end && m_items.size()) + { + if (current >= (int)m_items.size()) + break; + if (current >= 0) + { + CGUIListItemPtr item = m_items[current]; + bool focused = (current == m_offset * m_itemsPerRow + m_cursor) && m_bHasFocus; + // render our item + if (focused) + { + focusedPos = pos; + focusedCol = col; + focusedItem = item; + } + else + { + if (m_orientation == VERTICAL) + RenderItem(m_posX + col * m_layout->Size(HORIZONTAL), pos, item.get(), false); + else + RenderItem(pos, m_posY + col * m_layout->Size(VERTICAL), item.get(), false); + } + } + // increment our position + if (col < m_itemsPerRow - 1) + col++; + else + { + pos += m_layout->Size(m_orientation); + col = 0; + } + current++; + } + // and render the focused item last (for overlapping purposes) + if (focusedItem) + { + if (m_orientation == VERTICAL) + RenderItem(m_posX + focusedCol * m_layout->Size(HORIZONTAL), focusedPos, focusedItem.get(), true); + else + RenderItem(focusedPos, m_posY + focusedCol * m_layout->Size(VERTICAL), focusedItem.get(), true); + } + + g_graphicsContext.RestoreClipRegion(); + + UpdatePageControl(offset); + + CGUIControl::Render(); +} + +bool CGUIPanelContainer::OnAction(const CAction &action) +{ + switch (action.id) + { + case ACTION_PAGE_UP: + { + if (m_offset == 0) + { // already on the first page, so move to the first item + SetCursor(0); + } + else + { // scroll up to the previous page + Scroll( -m_itemsPerPage); + } + return true; + } + break; + case ACTION_PAGE_DOWN: + { + if ((m_offset + m_itemsPerPage) * m_itemsPerRow >= (int)m_items.size() || (int)m_items.size() < m_itemsPerPage) + { // already at the last page, so move to the last item. + SetCursor(m_items.size() - m_offset * m_itemsPerRow - 1); + } + else + { // scroll down to the next page + Scroll(m_itemsPerPage); + } + return true; + } + break; + // smooth scrolling (for analog controls) + case ACTION_SCROLL_UP: + { + m_analogScrollCount += action.amount1 * action.amount1; + bool handled = false; + while (m_analogScrollCount > AnalogScrollSpeed()) + { + handled = true; + m_analogScrollCount -= AnalogScrollSpeed(); + if (m_offset > 0)// && m_cursor <= m_itemsPerPage * m_itemsPerRow / 2) + { + Scroll(-1); + } + else if (m_cursor > 0) + { + SetCursor(m_cursor - 1); + } + } + return handled; + } + break; + case ACTION_SCROLL_DOWN: + { + m_analogScrollCount += action.amount1 * action.amount1; + bool handled = false; + while (m_analogScrollCount > AnalogScrollSpeed()) + { + handled = true; + m_analogScrollCount -= AnalogScrollSpeed(); + if ((m_offset + m_itemsPerPage) * m_itemsPerRow < (int)m_items.size())// && m_cursor >= m_itemsPerPage * m_itemsPerRow / 2) + { + Scroll(1); + } + else if (m_cursor < m_itemsPerPage * m_itemsPerRow - 1 && m_offset * m_itemsPerRow + m_cursor < (int)m_items.size() - 1) + { + SetCursor(m_cursor + 1); + } + } + return handled; + } + break; + } + return CGUIBaseContainer::OnAction(action); +} + +bool CGUIPanelContainer::OnMessage(CGUIMessage& message) +{ + if (message.GetControlId() == GetID() ) + { + if (message.GetMessage() == GUI_MSG_LABEL_RESET) + { + SetCursor(0); + // fall through to base class + } + else if (message.GetMessage() == GUI_MSG_ITEM_SELECT) + { + SelectItem(message.GetParam1()); + return true; + } + } + return CGUIBaseContainer::OnMessage(message); +} + +void CGUIPanelContainer::OnLeft() +{ + bool wrapAround = m_controlLeft == GetID() || !(m_controlLeft || m_leftActions.size()); + if (m_orientation == VERTICAL && MoveLeft(wrapAround)) + return; + if (m_orientation == HORIZONTAL && MoveUp(wrapAround)) + return; + CGUIControl::OnLeft(); +} + +void CGUIPanelContainer::OnRight() +{ + bool wrapAround = m_controlRight == GetID() || !(m_controlRight || m_rightActions.size()); + if (m_orientation == VERTICAL && MoveRight(wrapAround)) + return; + if (m_orientation == HORIZONTAL && MoveDown(wrapAround)) + return; + return CGUIControl::OnRight(); +} + +void CGUIPanelContainer::OnUp() +{ + bool wrapAround = m_controlUp == GetID() || !(m_controlUp || m_upActions.size()); + if (m_orientation == VERTICAL && MoveUp(wrapAround)) + return; + if (m_orientation == HORIZONTAL && MoveLeft(wrapAround)) + return; + CGUIControl::OnUp(); +} + +void CGUIPanelContainer::OnDown() +{ + bool wrapAround = m_controlDown == GetID() || !(m_controlDown || m_downActions.size()); + if (m_orientation == VERTICAL && MoveDown(wrapAround)) + return; + if (m_orientation == HORIZONTAL && MoveRight(wrapAround)) + return; + return CGUIControl::OnDown(); +} + +bool CGUIPanelContainer::MoveDown(bool wrapAround) +{ + if (m_cursor + m_itemsPerRow < m_itemsPerPage * m_itemsPerRow && (m_offset + 1 + m_cursor / m_itemsPerRow) * m_itemsPerRow < (int)m_items.size()) + { // move to last item if necessary + if ((m_offset + 1)*m_itemsPerRow + m_cursor >= (int)m_items.size()) + SetCursor((int)m_items.size() - 1 - m_offset*m_itemsPerRow); + else + SetCursor(m_cursor + m_itemsPerRow); + } + else if ((m_offset + 1 + m_cursor / m_itemsPerRow) * m_itemsPerRow < (int)m_items.size()) + { // we scroll to the next row, and move to last item if necessary + if ((m_offset + 1)*m_itemsPerRow + m_cursor >= (int)m_items.size()) + SetCursor((int)m_items.size() - 1 - (m_offset + 1)*m_itemsPerRow); + ScrollToOffset(m_offset + 1); + } + else if (wrapAround) + { // move first item in list + SetCursor(m_cursor % m_itemsPerRow); + ScrollToOffset(0); + g_infoManager.SetContainerMoving(GetID(), 1); + } + else + return false; + return true; +} + +bool CGUIPanelContainer::MoveUp(bool wrapAround) +{ + if (m_cursor >= m_itemsPerRow) + SetCursor(m_cursor - m_itemsPerRow); + else if (m_offset > 0) + ScrollToOffset(m_offset - 1); + else if (wrapAround) + { // move last item in list in this column + SetCursor((m_cursor % m_itemsPerRow) + (m_itemsPerPage - 1) * m_itemsPerRow); + int offset = max((int)GetRows() - m_itemsPerPage, 0); + // should check here whether cursor is actually allowed here, and reduce accordingly + if (offset * m_itemsPerRow + m_cursor >= (int)m_items.size()) + SetCursor((int)m_items.size() - offset * m_itemsPerRow - 1); + ScrollToOffset(offset); + g_infoManager.SetContainerMoving(GetID(), -1); + } + else + return false; + return true; +} + +bool CGUIPanelContainer::MoveLeft(bool wrapAround) +{ + int col = m_cursor % m_itemsPerRow; + if (col > 0) + SetCursor(m_cursor - 1); + else if (wrapAround) + { // wrap around + SetCursor(m_cursor + m_itemsPerRow - 1); + if (m_offset * m_itemsPerRow + m_cursor >= (int)m_items.size()) + SetCursor((int)m_items.size() - m_offset * m_itemsPerRow); + } + else + return false; + return true; +} + +bool CGUIPanelContainer::MoveRight(bool wrapAround) +{ + int col = m_cursor % m_itemsPerRow; + if (col + 1 < m_itemsPerRow && m_offset * m_itemsPerRow + m_cursor + 1 < (int)m_items.size()) + SetCursor(m_cursor + 1); + else if (wrapAround) // move first item in row + SetCursor(m_cursor - col); + else + return false; + return true; +} + +// scrolls the said amount +void CGUIPanelContainer::Scroll(int amount) +{ + // increase or decrease the offset + int offset = m_offset + amount; + if (offset > ((int)GetRows() - m_itemsPerPage) * m_itemsPerRow) + { + offset = ((int)GetRows() - m_itemsPerPage) * m_itemsPerRow; + } + if (offset < 0) offset = 0; + ScrollToOffset(offset); +} + +void CGUIPanelContainer::ValidateOffset() +{ // first thing is we check the range of m_offset + if (!m_layout) return; + if (m_offset > (int)GetRows() - m_itemsPerPage) + { + m_offset = (int)GetRows() - m_itemsPerPage; + m_scrollOffset = m_offset * m_layout->Size(m_orientation); + } + if (m_offset < 0) + { + m_offset = 0; + m_scrollOffset = 0; + } +} + +void CGUIPanelContainer::SetCursor(int cursor) +{ + // +1 to ensure we're OK if we have a half item + if (cursor > (m_itemsPerPage + 1)*m_itemsPerRow - 1) cursor = (m_itemsPerPage + 1)*m_itemsPerRow - 1; + if (cursor < 0) cursor = 0; + g_infoManager.SetContainerMoving(GetID(), cursor - m_cursor); + m_cursor = cursor; +} + +void CGUIPanelContainer::CalculateLayout() +{ + GetCurrentLayouts(); + + if (!m_layout || !m_focusedLayout) return; + // calculate the number of items to display + if (m_orientation == HORIZONTAL) + { + m_itemsPerRow = (int)(m_height / m_layout->Size(VERTICAL)); + m_itemsPerPage = (int)(m_width / m_layout->Size(HORIZONTAL)); + } + else + { + m_itemsPerRow = (int)(m_width / m_layout->Size(HORIZONTAL)); + m_itemsPerPage = (int)(m_height / m_layout->Size(VERTICAL)); + } + if (m_itemsPerRow < 1) m_itemsPerRow = 1; + if (m_itemsPerPage < 1) m_itemsPerPage = 1; + + // ensure that the scroll offset is a multiple of our size + m_scrollOffset = m_offset * m_layout->Size(m_orientation); +} + +unsigned int CGUIPanelContainer::GetRows() const +{ + assert(m_itemsPerRow > 0); + return (m_items.size() + m_itemsPerRow - 1) / m_itemsPerRow; +} + +float CGUIPanelContainer::AnalogScrollSpeed() const +{ + return 10.0f / m_itemsPerPage; +} + +int CGUIPanelContainer::CorrectOffset(int offset, int cursor) const +{ + return offset * m_itemsPerRow + cursor; +} + +bool CGUIPanelContainer::SelectItemFromPoint(const CPoint &point) +{ + if (!m_layout) + return false; + + float sizeX = m_orientation == VERTICAL ? m_layout->Size(HORIZONTAL) : m_layout->Size(VERTICAL); + float sizeY = m_orientation == VERTICAL ? m_layout->Size(VERTICAL) : m_layout->Size(HORIZONTAL); + + float posY = m_orientation == VERTICAL ? point.y : point.x; + for (int y = 0; y < m_itemsPerPage + 1; y++) // +1 to ensure if we have a half item we can select it + { + float posX = m_orientation == VERTICAL ? point.x : point.y; + for (int x = 0; x < m_itemsPerRow; x++) + { + int item = x + y * m_itemsPerRow; + if (posX < sizeX && posY < sizeY && item + m_offset < (int)m_items.size()) + { // found + SetCursor(item); + return true; + } + posX -= sizeX; + } + posY -= sizeY; + } + return false; +} + +bool CGUIPanelContainer::GetCondition(int condition, int data) const +{ // probably only works vertically atm... + int row = m_cursor / m_itemsPerRow; + int col = m_cursor % m_itemsPerRow; + if (m_orientation == HORIZONTAL) + swap(row, col); + switch (condition) + { + case CONTAINER_ROW: + return (row == data); + case CONTAINER_COLUMN: + return (col == data); + default: + return CGUIBaseContainer::GetCondition(condition, data); + } +} + +void CGUIPanelContainer::SelectItem(int item) +{ + // Check that m_offset is valid + ValidateOffset(); + // only select an item if it's in a valid range + if (item >= 0 && item < (int)m_items.size()) + { + // Select the item requested + if (item >= m_offset * m_itemsPerRow && item < (m_offset + m_itemsPerPage) * m_itemsPerRow) + { // the item is on the current page, so don't change it. + SetCursor(item - m_offset * m_itemsPerRow); + } + else if (item < m_offset * m_itemsPerRow) + { // item is on a previous page - make it the first item on the page + SetCursor(item % m_itemsPerRow); + ScrollToOffset((item - m_cursor) / m_itemsPerRow); + } + else // (item >= m_offset+m_itemsPerPage) + { // item is on a later page - make it the last row on the page + SetCursor(item % m_itemsPerRow + m_itemsPerRow * (m_itemsPerPage - 1)); + ScrollToOffset((item - m_cursor) / m_itemsPerRow); + } + } +} + +bool CGUIPanelContainer::HasPreviousPage() const +{ + return (m_offset > 0); +} + +bool CGUIPanelContainer::HasNextPage() const +{ + return (m_offset != (int)GetRows() - m_itemsPerPage && (int)GetRows() > m_itemsPerPage); +} + diff --git a/guilib/GUIPanelContainer.h b/guilib/GUIPanelContainer.h new file mode 100644 index 0000000000..86ff4cdf38 --- /dev/null +++ b/guilib/GUIPanelContainer.h @@ -0,0 +1,69 @@ +/*! +\file GUIPanelContainer.h +\brief +*/ + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIBaseContainer.h" + +/*! + \ingroup controls + \brief + */ +class CGUIPanelContainer : public CGUIBaseContainer +{ +public: + CGUIPanelContainer(int parentID, int controlID, float posX, float posY, float width, float height, ORIENTATION orientation, int scrollTime, int preloadItems); + virtual ~CGUIPanelContainer(void); + virtual CGUIPanelContainer *Clone() const { return new CGUIPanelContainer(*this); }; + + virtual void Render(); + virtual bool OnAction(const CAction &action); + virtual bool OnMessage(CGUIMessage& message); + virtual void OnLeft(); + virtual void OnRight(); + virtual void OnUp(); + virtual void OnDown(); + virtual bool GetCondition(int condition, int data) const; +protected: + virtual bool MoveUp(bool wrapAround); + virtual bool MoveDown(bool wrapAround); + virtual bool MoveLeft(bool wrapAround); + virtual bool MoveRight(bool wrapAround); + virtual void Scroll(int amount); + float AnalogScrollSpeed() const; + virtual void ValidateOffset(); + virtual void CalculateLayout(); + unsigned int GetRows() const; + virtual int CorrectOffset(int offset, int cursor) const; + virtual bool SelectItemFromPoint(const CPoint &point); + void SetCursor(int cursor); + virtual void SelectItem(int item); + virtual bool HasPreviousPage() const; + virtual bool HasNextPage() const; + + int m_itemsPerRow; +}; + diff --git a/guilib/GUIProgressControl.cpp b/guilib/GUIProgressControl.cpp new file mode 100644 index 0000000000..b491a06747 --- /dev/null +++ b/guilib/GUIProgressControl.cpp @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIProgressControl.h" +#include "utils/GUIInfoManager.h" + +CGUIProgressControl::CGUIProgressControl(int parentID, int controlID, + float posX, float posY, float width, + float height, const CTextureInfo& backGroundTexture, + const CTextureInfo& leftTexture, + const CTextureInfo& midTexture, + const CTextureInfo& rightTexture, + const CTextureInfo& overlayTexture, float min, float max, + bool reveal) + : CGUIControl(parentID, controlID, posX, posY, width, height) + , m_guiBackground(posX, posY, width, height, backGroundTexture) + , m_guiLeft(posX, posY, width, height, leftTexture) + , m_guiMid(posX, posY, width, height, midTexture) + , m_guiRight(posX, posY, width, height, rightTexture) + , m_guiOverlay(posX, posY, width, height, overlayTexture) +{ + m_RangeMin = min; + m_RangeMax = max; + m_fPercent = 0; + m_iInfoCode = 0; + ControlType = GUICONTROL_PROGRESS; + m_bReveal = reveal; +} + +CGUIProgressControl::~CGUIProgressControl(void) +{ +} + +void CGUIProgressControl::SetPosition(float posX, float posY) +{ + // everything is positioned based on the background image position + CGUIControl::SetPosition(posX, posY); + m_guiBackground.SetPosition(posX, posY); +} + +void CGUIProgressControl::Render() +{ + if (!IsDisabled()) + { + if (m_iInfoCode ) + { + m_fPercent = (float)g_infoManager.GetInt(m_iInfoCode); + if ((m_RangeMax - m_RangeMin)> 0 && (m_RangeMax != 100 && m_RangeMin != 0) ) + { + if (m_fPercent > m_RangeMax) + m_fPercent = m_RangeMax; + if (m_fPercent < m_RangeMin) + m_fPercent = m_RangeMin; + m_fPercent = ((100*(m_fPercent - m_RangeMin)) / (m_RangeMax - m_RangeMin)); + } + } + if (m_fPercent < 0.0f) m_fPercent = 0.0f; + if (m_fPercent > 100.0f) m_fPercent = 100.0f; + + float fScaleX, fScaleY; + if (m_width == 0) + m_width = m_guiBackground.GetTextureWidth(); + if (m_height == 0) + m_height = m_guiBackground.GetTextureHeight(); + fScaleY = m_height / m_guiBackground.GetTextureHeight(); + fScaleX = m_width / m_guiBackground.GetTextureWidth(); + + m_guiBackground.SetHeight(m_height); + m_guiBackground.SetWidth(m_width); + m_guiBackground.Render(); + + float fWidth = m_fPercent; + + float posX = m_guiBackground.GetXPosition(); + float posY = m_guiBackground.GetYPosition(); + + if (m_guiLeft.GetFileName().IsEmpty() && m_guiRight.GetFileName().IsEmpty()) + { // rendering without left and right image - fill the mid image completely + float width = m_fPercent * m_width * 0.01f; + if (m_fPercent && width > 1) + { + float offset = fabs(fScaleY * 0.5f * (m_guiMid.GetTextureHeight() - m_guiBackground.GetTextureHeight())); + if (offset > 0) // Center texture to the background if necessary + m_guiMid.SetPosition(posX, posY + offset); + else + m_guiMid.SetPosition(posX, posY); + m_guiMid.SetHeight(fScaleY * m_guiMid.GetTextureHeight()); + if (m_bReveal) + { + m_guiMid.SetWidth(m_width); + g_graphicsContext.SetClipRegion(posX, posY+offset, width, fScaleY * m_guiMid.GetTextureHeight()); + m_guiMid.Render(); + g_graphicsContext.RestoreClipRegion(); + } + else + { + m_guiMid.SetWidth(width); + m_guiMid.Render(); + } + posX += fWidth * fScaleX; + } + } + else + { + + float fWidth = m_fPercent; + float fFullWidth = m_guiBackground.GetTextureWidth() - m_guiLeft.GetTextureWidth() - m_guiRight.GetTextureWidth(); + fWidth /= 100.0f; + fWidth *= fFullWidth; + + float offset = fabs(fScaleY * 0.5f * (m_guiLeft.GetTextureHeight() - m_guiBackground.GetTextureHeight())); + if (offset > 0) // Center texture to the background if necessary + m_guiLeft.SetPosition(posX, posY + offset); + else + m_guiLeft.SetPosition(posX, posY); + m_guiLeft.SetHeight(fScaleY * m_guiLeft.GetTextureHeight()); + m_guiLeft.SetWidth(fScaleX * m_guiLeft.GetTextureWidth()); + m_guiLeft.Render(); + + posX += fScaleX * m_guiLeft.GetTextureWidth(); + if (m_fPercent && (int)(fScaleX * fWidth) > 1) + { + float offset = fabs(fScaleY * 0.5f * (m_guiMid.GetTextureHeight() - m_guiBackground.GetTextureHeight())); + if (offset > 0) // Center texture to the background if necessary + m_guiMid.SetPosition(posX, posY + offset); + else + m_guiMid.SetPosition(posX, posY); + m_guiMid.SetHeight(fScaleY * m_guiMid.GetTextureHeight()); + if (m_bReveal) + { + m_guiMid.SetWidth(fScaleX * fFullWidth); + g_graphicsContext.SetClipRegion(posX, posY+offset, fScaleX * fWidth, fScaleY * m_guiMid.GetTextureHeight()); + m_guiMid.Render(); + g_graphicsContext.RestoreClipRegion(); + } + else + { + m_guiMid.SetWidth(fScaleX * fWidth); + m_guiMid.Render(); + } + posX += fWidth * fScaleX; + } + + offset = fabs(fScaleY * 0.5f * (m_guiRight.GetTextureHeight() - m_guiBackground.GetTextureHeight())); + if (offset > 0) // Center texture to the background if necessary + m_guiRight.SetPosition(posX, posY + offset); + else + m_guiRight.SetPosition(posX, posY); + m_guiRight.SetHeight(fScaleY * m_guiRight.GetTextureHeight()); + m_guiRight.SetWidth(fScaleX * m_guiRight.GetTextureWidth()); + m_guiRight.Render(); + } + float offset = fabs(fScaleY * 0.5f * (m_guiOverlay.GetTextureHeight() - m_guiBackground.GetTextureHeight())); + if (offset > 0) // Center texture to the background if necessary + m_guiOverlay.SetPosition(m_guiBackground.GetXPosition(), m_guiBackground.GetYPosition() + offset); + else + m_guiOverlay.SetPosition(m_guiBackground.GetXPosition(), m_guiBackground.GetYPosition()); + m_guiOverlay.SetHeight(fScaleY * m_guiOverlay.GetTextureHeight()); + m_guiOverlay.SetWidth(fScaleX * m_guiOverlay.GetTextureWidth()); + m_guiOverlay.Render(); + } + CGUIControl::Render(); +} + + +bool CGUIProgressControl::CanFocus() const +{ + return false; +} + + +bool CGUIProgressControl::OnMessage(CGUIMessage& message) +{ + return CGUIControl::OnMessage(message); +} + +void CGUIProgressControl::SetPercentage(float fPercent) +{ + m_fPercent = fPercent; +} + +float CGUIProgressControl::GetPercentage() const +{ + return m_fPercent; +} +void CGUIProgressControl::FreeResources() +{ + CGUIControl::FreeResources(); + m_guiBackground.FreeResources(); + m_guiMid.FreeResources(); + m_guiRight.FreeResources(); + m_guiLeft.FreeResources(); + m_guiOverlay.FreeResources(); +} + +void CGUIProgressControl::DynamicResourceAlloc(bool bOnOff) +{ + CGUIControl::DynamicResourceAlloc(bOnOff); + m_guiBackground.DynamicResourceAlloc(bOnOff); + m_guiMid.DynamicResourceAlloc(bOnOff); + m_guiRight.DynamicResourceAlloc(bOnOff); + m_guiLeft.DynamicResourceAlloc(bOnOff); + m_guiOverlay.DynamicResourceAlloc(bOnOff); +} + +void CGUIProgressControl::AllocResources() +{ + CGUIControl::AllocResources(); + m_guiBackground.AllocResources(); + m_guiMid.AllocResources(); + m_guiRight.AllocResources(); + m_guiLeft.AllocResources(); + m_guiOverlay.AllocResources(); + + m_guiBackground.SetHeight(25); + m_guiRight.SetHeight(20); + m_guiLeft.SetHeight(20); + m_guiMid.SetHeight(20); + m_guiOverlay.SetHeight(20); +} + +void CGUIProgressControl::SetInfo(int iInfo) +{ + m_iInfoCode = iInfo; +} + +void CGUIProgressControl::UpdateColors() +{ + CGUIControl::UpdateColors(); + m_guiBackground.SetDiffuseColor(m_diffuseColor); + m_guiRight.SetDiffuseColor(m_diffuseColor); + m_guiLeft.SetDiffuseColor(m_diffuseColor); + m_guiMid.SetDiffuseColor(m_diffuseColor); + m_guiOverlay.SetDiffuseColor(m_diffuseColor); +} + +CStdString CGUIProgressControl::GetDescription() const +{ + CStdString percent; + percent.Format("%2.f", m_fPercent); + return percent; +} diff --git a/guilib/GUIProgressControl.h b/guilib/GUIProgressControl.h new file mode 100644 index 0000000000..25bf9e4c5d --- /dev/null +++ b/guilib/GUIProgressControl.h @@ -0,0 +1,77 @@ +/*! +\file GUIProgressControl.h +\brief +*/ + +#ifndef GUILIB_GUIPROGRESSCONTROL_H +#define GUILIB_GUIPROGRESSCONTROL_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUITexture.h" +#include "GUIControl.h" + +/*! + \ingroup controls + \brief + */ +class CGUIProgressControl : + public CGUIControl +{ +public: + CGUIProgressControl(int parentID, int controlID, float posX, float posY, + float width, float height, const CTextureInfo& backGroundTexture, + const CTextureInfo& leftTexture, const CTextureInfo& midTexture, + const CTextureInfo& rightTexture, const CTextureInfo& overlayTexture, + float min, float max, bool reveal=false); + virtual ~CGUIProgressControl(void); + virtual CGUIProgressControl *Clone() const { return new CGUIProgressControl(*this); }; + + virtual void Render(); + virtual bool CanFocus() const; + virtual void AllocResources(); + virtual void FreeResources(); + virtual void DynamicResourceAlloc(bool bOnOff); + virtual bool OnMessage(CGUIMessage& message); + virtual void SetPosition(float posX, float posY); + void SetPercentage(float fPercent); + void SetInfo(int iInfo); + int GetInfo() const {return m_iInfoCode;}; + + float GetPercentage() const; + CStdString GetDescription() const; +protected: + virtual void UpdateColors(); + CGUITexture m_guiBackground; + CGUITexture m_guiLeft; + CGUITexture m_guiMid; + CGUITexture m_guiRight; + CGUITexture m_guiOverlay; + float m_RangeMin; + float m_RangeMax; + int m_iInfoCode; + float m_fPercent; + bool m_bReveal; +}; +#endif diff --git a/guilib/GUIRSSControl.cpp b/guilib/GUIRSSControl.cpp new file mode 100644 index 0000000000..9f30312c98 --- /dev/null +++ b/guilib/GUIRSSControl.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIRSSControl.h" +#include "GUIWindowManager.h" +#include "GUISettings.h" +#include "utils/CriticalSection.h" +#include "utils/SingleLock.h" +#include "utils/RssReader.h" +#include "StringUtils.h" + +using namespace std; + +CGUIRSSControl::CGUIRSSControl(int parentID, int controlID, float posX, float posY, float width, float height, const CLabelInfo& labelInfo, const CGUIInfoColor &channelColor, const CGUIInfoColor &headlineColor, CStdString& strRSSTags, int scrollSpeed) +: CGUIControl(parentID, controlID, posX, posY, width, height), + m_scrollInfo(0,0,scrollSpeed,"") +{ + m_label = labelInfo; + m_headlineColor = headlineColor; + m_channelColor = channelColor; + + m_strRSSTags = strRSSTags; + + m_pReader = NULL; + ControlType = GUICONTROL_RSS; + + if (g_guiSettings.GetBool("lookandfeel.rssfeedsrtl")) + { + m_scrollInfo.SetSpeed(-60); + } +} + +CGUIRSSControl::CGUIRSSControl(const CGUIRSSControl &from) +: CGUIControl(from),m_scrollInfo(from.m_scrollInfo) +{ + m_label = from.m_label; + m_headlineColor = from.m_headlineColor; + m_channelColor = from.m_channelColor; + m_strRSSTags = from.m_strRSSTags; + m_pReader = NULL; + ControlType = GUICONTROL_RSS; +} + +CGUIRSSControl::~CGUIRSSControl(void) +{ + CSingleLock lock(m_criticalSection); + if (m_pReader) + m_pReader->SetObserver(NULL); + m_pReader = NULL; +} + +void CGUIRSSControl::SetUrls(const vector<string> &vecUrl) +{ + m_vecUrls = vecUrl; +} + +void CGUIRSSControl::SetIntervals(const vector<int>& vecIntervals) +{ + m_vecIntervals = vecIntervals; +} + +void CGUIRSSControl::UpdateColors() +{ + m_label.UpdateColors(); + m_headlineColor.Update(); + m_channelColor.Update(); + CGUIControl::UpdateColors(); +} + +void CGUIRSSControl::Render() +{ + // only render the control if they are enabled + if (g_guiSettings.GetBool("lookandfeel.enablerssfeeds") && g_rssManager.IsActive()) + { + CSingleLock lock(m_criticalSection); + // Create RSS background/worker thread if needed + if (m_pReader == NULL) + { + if (g_rssManager.GetReader(GetID(), GetParentID(), this, m_pReader)) + m_scrollInfo.characterPos = m_pReader->m_SavedScrollPos; + else + { + if (m_strRSSTags != "") + { + CStdStringArray vecSplitTags; + + StringUtils::SplitString(m_strRSSTags, ",", vecSplitTags); + + for (unsigned int i = 0;i < vecSplitTags.size();i++) + m_pReader->AddTag(vecSplitTags[i]); + } + // use half the width of the control as spacing between feeds, and double this between feed sets + float spaceWidth = (m_label.font) ? m_label.font->GetCharWidth(L' ') : 15; + m_pReader->Create(this, m_vecUrls, m_vecIntervals, (int)(0.5f*GetWidth() / spaceWidth) + 1); + } + } + + if (m_label.font) + { + vecColors colors; + colors.push_back(m_label.textColor); + colors.push_back(m_headlineColor); + colors.push_back(m_channelColor); + m_label.font->DrawScrollingText(m_posX, m_posY, colors, m_label.shadowColor, m_feed, 0, m_width, m_scrollInfo); + } + + if (m_pReader) + { + m_pReader->CheckForUpdates(); + m_pReader->m_SavedScrollPos = m_scrollInfo.characterPos; + } + } + CGUIControl::Render(); +} + +void CGUIRSSControl::OnFeedUpdate(const vecText &feed) +{ + CSingleLock lock(m_criticalSection); + m_feed = feed; +} + +void CGUIRSSControl::OnFeedRelease() +{ + m_pReader = NULL; +} + + diff --git a/guilib/GUIRSSControl.h b/guilib/GUIRSSControl.h new file mode 100644 index 0000000000..8d3aa4e0db --- /dev/null +++ b/guilib/GUIRSSControl.h @@ -0,0 +1,87 @@ +/*! +\file GUIRSSControl.h +\brief +*/ + +#ifndef GUILIB_GUIRSSControl_H +#define GUILIB_GUIRSSControl_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControl.h" + +typedef uint32_t character_t; +typedef uint32_t color_t; +typedef std::vector<character_t> vecText; +typedef std::vector<color_t> vecColors; + +class CRssReader; + +class IRssObserver +{ +public: + virtual void OnFeedUpdate(const vecText &feed) = 0; + virtual void OnFeedRelease() = 0; + virtual ~IRssObserver() {} +}; + +/*! +\ingroup controls +\brief +*/ +class CGUIRSSControl : public CGUIControl, public IRssObserver +{ +public: + CGUIRSSControl(int parentID, int controlID, float posX, float posY, float width, float height, const CLabelInfo& labelInfo, const CGUIInfoColor &channelColor, const CGUIInfoColor &headlineColor, CStdString& strRSSTags, int scrollSpeed); + CGUIRSSControl(const CGUIRSSControl &from); + virtual ~CGUIRSSControl(void); + virtual CGUIRSSControl *Clone() const { return new CGUIRSSControl(*this); }; + + virtual void Render(); + virtual void OnFeedUpdate(const vecText &feed); + virtual void OnFeedRelease(); + virtual bool CanFocus() const { return false; }; + + void SetIntervals(const std::vector<int>& vecIntervals); + void SetUrls(const std::vector<std::string>& vecUrl); + +protected: + virtual void UpdateColors(); + + CCriticalSection m_criticalSection; + + CRssReader* m_pReader; + vecText m_feed; + + CStdString m_strRSSTags; + + CLabelInfo m_label; + CGUIInfoColor m_channelColor; + CGUIInfoColor m_headlineColor; + + std::vector<std::string> m_vecUrls; + std::vector<int> m_vecIntervals; + CScrollInfo m_scrollInfo; +}; +#endif diff --git a/guilib/GUIRadioButtonControl.cpp b/guilib/GUIRadioButtonControl.cpp new file mode 100644 index 0000000000..43111eb571 --- /dev/null +++ b/guilib/GUIRadioButtonControl.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIRadioButtonControl.h" +#include "utils/GUIInfoManager.h" +#include "GUIFontManager.h" +#include "Key.h" + +CGUIRadioButtonControl::CGUIRadioButtonControl(int parentID, int controlID, float posX, float posY, float width, float height, + const CTextureInfo& textureFocus, const CTextureInfo& textureNoFocus, + const CLabelInfo& labelInfo, + const CTextureInfo& radioOn, const CTextureInfo& radioOff) + : CGUIButtonControl(parentID, controlID, posX, posY, width, height, textureFocus, textureNoFocus, labelInfo) + , m_imgRadioOn(posX, posY, 16, 16, radioOn) + , m_imgRadioOff(posX, posY, 16, 16, radioOff) +{ + m_radioPosX = 0; + m_radioPosY = 0; + m_toggleSelect = 0; + m_imgRadioOn.SetAspectRatio(CAspectRatio::AR_KEEP);\ + m_imgRadioOff.SetAspectRatio(CAspectRatio::AR_KEEP); + ControlType = GUICONTROL_RADIO; +} + +CGUIRadioButtonControl::~CGUIRadioButtonControl(void) +{} + + +void CGUIRadioButtonControl::Render() +{ + CGUIButtonControl::Render(); + + // ask our infoManager whether we are selected or not... + if (m_toggleSelect) + m_bSelected = g_infoManager.GetBool(m_toggleSelect, m_parentID); + + if ( IsSelected() && !IsDisabled() ) + m_imgRadioOn.Render(); + else + m_imgRadioOff.Render(); +} + +bool CGUIRadioButtonControl::OnAction(const CAction &action) +{ + if (action.id == ACTION_SELECT_ITEM) + { + m_bSelected = !m_bSelected; + } + return CGUIButtonControl::OnAction(action); +} + +bool CGUIRadioButtonControl::OnMessage(CGUIMessage& message) +{ + return CGUIButtonControl::OnMessage(message); +} + +void CGUIRadioButtonControl::AllocResources() +{ + CGUIButtonControl::AllocResources(); + m_imgRadioOn.AllocResources(); + m_imgRadioOff.AllocResources(); + + SetPosition(m_posX, m_posY); +} + +void CGUIRadioButtonControl::FreeResources() +{ + CGUIButtonControl::FreeResources(); + m_imgRadioOn.FreeResources(); + m_imgRadioOff.FreeResources(); +} + +void CGUIRadioButtonControl::DynamicResourceAlloc(bool bOnOff) +{ + CGUIControl::DynamicResourceAlloc(bOnOff); + m_imgRadioOn.DynamicResourceAlloc(bOnOff); + m_imgRadioOff.DynamicResourceAlloc(bOnOff); +} + +void CGUIRadioButtonControl::SetPosition(float posX, float posY) +{ + CGUIButtonControl::SetPosition(posX, posY); + float radioPosX = m_radioPosX ? m_posX + m_radioPosX : (m_posX + m_width - 8) - m_imgRadioOn.GetWidth(); + float radioPosY = m_radioPosY ? m_posY + m_radioPosY : m_posY + (m_height - m_imgRadioOn.GetHeight()) / 2; + m_imgRadioOn.SetPosition(radioPosX, radioPosY); + m_imgRadioOff.SetPosition(radioPosX, radioPosY); +} + +void CGUIRadioButtonControl::SetRadioDimensions(float posX, float posY, float width, float height) +{ + m_radioPosX = posX; + m_radioPosY = posY; + if (width) + { + m_imgRadioOn.SetWidth(width); + m_imgRadioOff.SetWidth(width); + } + if (height) + { + m_imgRadioOn.SetHeight(height); + m_imgRadioOff.SetHeight(height); + } + SetPosition(GetXPosition(), GetYPosition()); +} + +void CGUIRadioButtonControl::SetWidth(float width) +{ + CGUIButtonControl::SetWidth(width); + SetPosition(GetXPosition(), GetYPosition()); +} + +void CGUIRadioButtonControl::SetHeight(float height) +{ + CGUIButtonControl::SetHeight(height); + SetPosition(GetXPosition(), GetYPosition()); +} + +CStdString CGUIRadioButtonControl::GetDescription() const +{ + CStdString strLabel = CGUIButtonControl::GetDescription(); + if (m_bSelected) + strLabel += " (*)"; + else + strLabel += " ( )"; + return strLabel; +} + +void CGUIRadioButtonControl::UpdateColors() +{ + CGUIButtonControl::UpdateColors(); + m_imgRadioOn.SetDiffuseColor(m_diffuseColor); + m_imgRadioOff.SetDiffuseColor(m_diffuseColor); +} + diff --git a/guilib/GUIRadioButtonControl.h b/guilib/GUIRadioButtonControl.h new file mode 100644 index 0000000000..80dba57873 --- /dev/null +++ b/guilib/GUIRadioButtonControl.h @@ -0,0 +1,68 @@ +/*! +\file GUIRadioButtonControl.h +\brief +*/ + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIButtonControl.h" + +/*! + \ingroup controls + \brief + */ +class CGUIRadioButtonControl : + public CGUIButtonControl +{ +public: + CGUIRadioButtonControl(int parentID, int controlID, + float posX, float posY, float width, float height, + const CTextureInfo& textureFocus, const CTextureInfo& textureNoFocus, + const CLabelInfo& labelInfo, + const CTextureInfo& radioOn, const CTextureInfo& radioOff); + + virtual ~CGUIRadioButtonControl(void); + virtual CGUIRadioButtonControl *Clone() const { return new CGUIRadioButtonControl(*this); }; + + virtual void Render(); + virtual bool OnAction(const CAction &action) ; + virtual bool OnMessage(CGUIMessage& message); + virtual void AllocResources(); + virtual void FreeResources(); + virtual void DynamicResourceAlloc(bool bOnOff); + virtual void SetPosition(float posX, float posY); + virtual void SetWidth(float width); + virtual void SetHeight(float height); + virtual CStdString GetDescription() const; + void SetRadioDimensions(float posX, float posY, float width, float height); + void SetToggleSelect(int toggleSelect) { m_toggleSelect = toggleSelect; }; + bool IsSelected() const { return m_bSelected; }; +protected: + virtual void UpdateColors(); + CGUITexture m_imgRadioOn; + CGUITexture m_imgRadioOff; + float m_radioPosX; + float m_radioPosY; + int m_toggleSelect; +}; diff --git a/guilib/GUIResizeControl.cpp b/guilib/GUIResizeControl.cpp new file mode 100644 index 0000000000..03d8b2106e --- /dev/null +++ b/guilib/GUIResizeControl.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIResizeControl.h" +#include "GUIWindowManager.h" +#include "MouseStat.h" +#include "Key.h" + +// time to reset accelerated cursors (digital movement) +#define MOVE_TIME_OUT 500L + +CGUIResizeControl::CGUIResizeControl(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& textureFocus, const CTextureInfo& textureNoFocus) + : CGUIControl(parentID, controlID, posX, posY, width, height) + , m_imgFocus(posX, posY, width, height, textureFocus) + , m_imgNoFocus(posX, posY, width, height, textureNoFocus) +{ + m_dwFrameCounter = 0; + m_dwLastMoveTime = 0; + m_fSpeed = 1.0; + m_fAnalogSpeed = 2.0f; // TODO: implement correct analog speed + m_fAcceleration = 0.2f; // TODO: implement correct computation of acceleration + m_fMaxSpeed = 10.0; // TODO: implement correct computation of maxspeed + ControlType = GUICONTROL_RESIZE; + SetLimits(0, 0, 720, 576); // defaults +} + +CGUIResizeControl::~CGUIResizeControl(void) +{} + +void CGUIResizeControl::Render() +{ + if (m_bInvalidated) + { + m_imgFocus.SetWidth(m_width); + m_imgFocus.SetHeight(m_height); + + m_imgNoFocus.SetWidth(m_width); + m_imgNoFocus.SetHeight(m_height); + } + if (HasFocus()) + { + DWORD dwAlphaCounter = m_dwFrameCounter + 2; + DWORD dwAlphaChannel; + if ((dwAlphaCounter % 128) >= 64) + dwAlphaChannel = dwAlphaCounter % 64; + else + dwAlphaChannel = 63 - (dwAlphaCounter % 64); + + dwAlphaChannel += 192; + SetAlpha( (unsigned char)dwAlphaChannel ); + m_imgFocus.SetVisible(true); + m_imgNoFocus.SetVisible(false); + m_dwFrameCounter++; + } + else + { + SetAlpha(0xff); + m_imgFocus.SetVisible(false); + m_imgNoFocus.SetVisible(true); + } + // render both so the visibility settings cause the frame counter to resetcorrectly + m_imgFocus.Render(); + m_imgNoFocus.Render(); + CGUIControl::Render(); +} + +bool CGUIResizeControl::OnAction(const CAction &action) +{ + if (action.id == ACTION_SELECT_ITEM) + { + // button selected - send message to parent + CGUIMessage message(GUI_MSG_CLICKED, GetID(), GetParentID()); + SendWindowMessage(message); + return true; + } + if (action.id == ACTION_ANALOG_MOVE) + { + Resize(m_fAnalogSpeed*action.amount1, -m_fAnalogSpeed*action.amount2); + return true; + } + return CGUIControl::OnAction(action); +} + +void CGUIResizeControl::OnUp() +{ + UpdateSpeed(DIRECTION_UP); + Resize(0, -m_fSpeed); +} + +void CGUIResizeControl::OnDown() +{ + UpdateSpeed(DIRECTION_DOWN); + Resize(0, m_fSpeed); +} + +void CGUIResizeControl::OnLeft() +{ + UpdateSpeed(DIRECTION_LEFT); + Resize(-m_fSpeed, 0); +} + +void CGUIResizeControl::OnRight() +{ + UpdateSpeed(DIRECTION_RIGHT); + Resize(m_fSpeed, 0); +} + +bool CGUIResizeControl::OnMouseDrag(const CPoint &offset, const CPoint &point) +{ + g_Mouse.SetState(MOUSE_STATE_DRAG); + g_Mouse.SetExclusiveAccess(GetID(), GetParentID(), point); + Resize(offset.x, offset.y); + return true; +} + +bool CGUIResizeControl::OnMouseClick(int button, const CPoint &point) +{ + if (button != MOUSE_LEFT_BUTTON) return false; + g_Mouse.EndExclusiveAccess(GetID(), GetParentID()); + return true; +} + +void CGUIResizeControl::UpdateSpeed(int nDirection) +{ + if (timeGetTime() - m_dwLastMoveTime > MOVE_TIME_OUT) + { + m_fSpeed = 1; + m_nDirection = DIRECTION_NONE; + } + m_dwLastMoveTime = timeGetTime(); + if (nDirection == m_nDirection) + { // accelerate + m_fSpeed += m_fAcceleration; + if (m_fSpeed > m_fMaxSpeed) m_fSpeed = m_fMaxSpeed; + } + else + { // reset direction and speed + m_fSpeed = 1; + m_nDirection = nDirection; + } +} + +void CGUIResizeControl::AllocResources() +{ + CGUIControl::AllocResources(); + m_dwFrameCounter = 0; + m_imgFocus.AllocResources(); + m_imgNoFocus.AllocResources(); + m_width = m_imgFocus.GetWidth(); + m_height = m_imgFocus.GetHeight(); +} + +void CGUIResizeControl::FreeResources() +{ + CGUIControl::FreeResources(); + m_imgFocus.FreeResources(); + m_imgNoFocus.FreeResources(); +} + +void CGUIResizeControl::DynamicResourceAlloc(bool bOnOff) +{ + CGUIControl::DynamicResourceAlloc(bOnOff); + m_imgFocus.DynamicResourceAlloc(bOnOff); + m_imgNoFocus.DynamicResourceAlloc(bOnOff); +} + +void CGUIResizeControl::Resize(float x, float y) +{ + float width = m_width + x; + float height = m_height + y; + // check if we are within the bounds + if (width < m_x1) width = m_x1; + if (height < m_y1) height = m_y1; + if (width > m_x2) width = m_x2; + if (height > m_y2) height = m_y2; + // ok, now set the default size of the resize control + SetWidth(width); + SetHeight(height); +} + +void CGUIResizeControl::SetPosition(float posX, float posY) +{ + CGUIControl::SetPosition(posX, posY); + m_imgFocus.SetPosition(posX, posY); + m_imgNoFocus.SetPosition(posX, posY); +} + +void CGUIResizeControl::SetAlpha(unsigned char alpha) +{ + m_imgFocus.SetAlpha(alpha); + m_imgNoFocus.SetAlpha(alpha); +} + +void CGUIResizeControl::UpdateColors() +{ + CGUIControl::UpdateColors(); + m_imgFocus.SetDiffuseColor(m_diffuseColor); + m_imgNoFocus.SetDiffuseColor(m_diffuseColor); +} + +void CGUIResizeControl::SetLimits(float x1, float y1, float x2, float y2) +{ + m_x1 = x1; + m_y1 = y1; + m_x2 = x2; + m_y2 = y2; +} diff --git a/guilib/GUIResizeControl.h b/guilib/GUIResizeControl.h new file mode 100644 index 0000000000..452bd72aaf --- /dev/null +++ b/guilib/GUIResizeControl.h @@ -0,0 +1,85 @@ +/*! +\file GUIRESIZEControl.h +\brief +*/ + +#ifndef GUILIB_GUIRESIZECONTROL_H +#define GUILIB_GUIRESIZECONTROL_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUITexture.h" +#include "GUIControl.h" + +#define DIRECTION_NONE 0 +#define DIRECTION_UP 1 +#define DIRECTION_DOWN 2 +#define DIRECTION_LEFT 3 +#define DIRECTION_RIGHT 4 + +/*! + \ingroup controls + \brief + */ +class CGUIResizeControl : public CGUIControl +{ +public: + CGUIResizeControl(int parentID, int controlID, + float posX, float posY, float width, float height, + const CTextureInfo& textureFocus, const CTextureInfo& textureNoFocus); + + virtual ~CGUIResizeControl(void); + virtual CGUIResizeControl *Clone() const { return new CGUIResizeControl(*this); }; + + virtual void Render(); + virtual bool OnAction(const CAction &action); + virtual void OnUp(); + virtual void OnDown(); + virtual void OnLeft(); + virtual void OnRight(); + virtual bool OnMouseDrag(const CPoint &offset, const CPoint &point); + virtual bool OnMouseClick(int button, const CPoint &point); + virtual void AllocResources(); + virtual void FreeResources(); + virtual void DynamicResourceAlloc(bool bOnOff); + virtual void SetPosition(float posX, float posY); + void SetLimits(float x1, float y1, float x2, float y2); + +protected: + virtual void UpdateColors(); + void SetAlpha(unsigned char alpha); + void UpdateSpeed(int nDirection); + void Resize(float x, float y); + CGUITexture m_imgFocus; + CGUITexture m_imgNoFocus; + DWORD m_dwFrameCounter; + DWORD m_dwLastMoveTime; + int m_nDirection; + float m_fSpeed; + float m_fAnalogSpeed; + float m_fMaxSpeed; + float m_fAcceleration; + float m_x1, m_x2, m_y1, m_y2; +}; +#endif diff --git a/guilib/GUIScrollBarControl.cpp b/guilib/GUIScrollBarControl.cpp new file mode 100644 index 0000000000..9cd7125367 --- /dev/null +++ b/guilib/GUIScrollBarControl.cpp @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIScrollBarControl.h" +#include "MouseStat.h" +#include "Key.h" + +#define MIN_NIB_SIZE 4.0f + +CGUIScrollBar::CGUIScrollBar(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& backGroundTexture, const CTextureInfo& barTexture, const CTextureInfo& barTextureFocus, const CTextureInfo& nibTexture, const CTextureInfo& nibTextureFocus, ORIENTATION orientation, bool showOnePage) + : CGUIControl(parentID, controlID, posX, posY, width, height) + , m_guiBackground(posX, posY, width, height, backGroundTexture) + , m_guiBarNoFocus(posX, posY, width, height, barTexture) + , m_guiBarFocus(posX, posY, width, height, barTextureFocus) + , m_guiNibNoFocus(posX, posY, width, height, nibTexture) + , m_guiNibFocus(posX, posY, width, height, nibTextureFocus) +{ + m_guiNibNoFocus.SetAspectRatio(CAspectRatio::AR_CENTER); + m_guiNibFocus.SetAspectRatio(CAspectRatio::AR_CENTER); + m_numItems = 100; + m_offset = 0; + m_pageSize = 10; + ControlType = GUICONTROL_SCROLLBAR; + m_orientation = orientation; + m_showOnePage = showOnePage; +} + +CGUIScrollBar::~CGUIScrollBar(void) +{ +} + + +void CGUIScrollBar::Render() +{ + if (m_bInvalidated) + UpdateBarSize(); + + m_guiBackground.Render(); + if (m_bHasFocus) + { + m_guiBarFocus.Render(); + m_guiNibFocus.Render(); + } + else + { + m_guiBarNoFocus.Render(); + m_guiNibNoFocus.Render(); + } + + CGUIControl::Render(); +} + +bool CGUIScrollBar::OnMessage(CGUIMessage& message) +{ + switch (message.GetMessage()) + { + case GUI_MSG_ITEM_SELECT: + SetValue(message.GetParam1()); + return true; + case GUI_MSG_LABEL_RESET: + SetRange(message.GetParam1(), message.GetParam2()); + return true; + case GUI_MSG_PAGE_UP: + Move(-1); + return true; + case GUI_MSG_PAGE_DOWN: + Move(1); + return true; + } + return CGUIControl::OnMessage(message); +} + +bool CGUIScrollBar::OnAction(const CAction &action) +{ + switch ( action.id ) + { + case ACTION_MOVE_LEFT: + if (m_orientation == HORIZONTAL) + { + Move( -1); + return true; + } + break; + + case ACTION_MOVE_RIGHT: + if (m_orientation == HORIZONTAL) + { + Move(1); + return true; + } + break; + case ACTION_MOVE_UP: + if (m_orientation == VERTICAL) + { + Move(-1); + return true; + } + break; + + case ACTION_MOVE_DOWN: + if (m_orientation == VERTICAL) + { + Move(1); + return true; + } + break; + } + return CGUIControl::OnAction(action); +} + +void CGUIScrollBar::Move(int numSteps) +{ + m_offset += numSteps * m_pageSize; + if (m_offset > m_numItems - m_pageSize) m_offset = m_numItems - m_pageSize; + if (m_offset < 0) m_offset = 0; + CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetParentID(), GetID(), GUI_MSG_PAGE_CHANGE, m_offset); + SendWindowMessage(message); + SetInvalid(); +} + +void CGUIScrollBar::SetRange(int pageSize, int numItems) +{ + m_pageSize = pageSize; + m_numItems = numItems; + m_offset = 0; + SetInvalid(); +} + +void CGUIScrollBar::SetValue(int value) +{ + m_offset = value; + SetInvalid(); +} + +void CGUIScrollBar::FreeResources() +{ + CGUIControl::FreeResources(); + m_guiBackground.FreeResources(); + m_guiBarNoFocus.FreeResources(); + m_guiBarFocus.FreeResources(); + m_guiNibNoFocus.FreeResources(); + m_guiNibFocus.FreeResources(); +} + +void CGUIScrollBar::DynamicResourceAlloc(bool bOnOff) +{ + CGUIControl::DynamicResourceAlloc(bOnOff); + m_guiBackground.DynamicResourceAlloc(bOnOff); + m_guiBarNoFocus.DynamicResourceAlloc(bOnOff); + m_guiBarFocus.DynamicResourceAlloc(bOnOff); + m_guiNibNoFocus.DynamicResourceAlloc(bOnOff); + m_guiNibFocus.DynamicResourceAlloc(bOnOff); +} + +void CGUIScrollBar::AllocResources() +{ + CGUIControl::AllocResources(); + m_guiBackground.AllocResources(); + m_guiBarNoFocus.AllocResources(); + m_guiBarFocus.AllocResources(); + m_guiNibNoFocus.AllocResources(); + m_guiNibFocus.AllocResources(); +} + +void CGUIScrollBar::UpdateBarSize() +{ + // scale our textures to suit + if (m_orientation == VERTICAL) + { + // calculate the height to display the nib at + float percent = (float)m_pageSize / m_numItems; + float nibSize = GetHeight() * percent; + if (nibSize < m_guiNibFocus.GetTextureHeight() + 2 * MIN_NIB_SIZE) nibSize = m_guiNibFocus.GetTextureHeight() + 2 * MIN_NIB_SIZE; + if (nibSize > GetHeight()) nibSize = GetHeight(); + + m_guiBarNoFocus.SetHeight(nibSize); + m_guiBarFocus.SetHeight(nibSize); + m_guiNibNoFocus.SetHeight(nibSize); + m_guiNibFocus.SetHeight(nibSize); + // nibSize may be altered by the border size of the nib (and bar). + nibSize = std::max(m_guiBarFocus.GetHeight(), m_guiNibFocus.GetHeight()); + + // and the position + percent = (m_numItems == m_pageSize) ? 0 : (float)m_offset / (m_numItems - m_pageSize); + float nibPos = (GetHeight() - nibSize) * percent; + if (nibPos < 0) nibPos = 0; + if (nibPos > GetHeight() - nibSize) nibPos = GetHeight() - nibSize; + m_guiBarNoFocus.SetPosition(GetXPosition(), GetYPosition() + nibPos); + m_guiBarFocus.SetPosition(GetXPosition(), GetYPosition() + nibPos); + m_guiNibNoFocus.SetPosition(GetXPosition(), GetYPosition() + nibPos); + m_guiNibFocus.SetPosition(GetXPosition(), GetYPosition() + nibPos); + } + else + { + // calculate the height to display the nib at + float percent = (float)m_pageSize / m_numItems; + float nibSize = GetWidth() * percent + 0.5f; + if (nibSize < m_guiNibFocus.GetTextureWidth() + 2 * MIN_NIB_SIZE) nibSize = m_guiNibFocus.GetTextureWidth() + 2 * MIN_NIB_SIZE; + if (nibSize > GetWidth()) nibSize = GetWidth(); + + m_guiBarNoFocus.SetWidth(nibSize); + m_guiBarFocus.SetWidth(nibSize); + m_guiNibNoFocus.SetWidth(nibSize); + m_guiNibFocus.SetWidth(nibSize); + + // and the position + percent = (float)m_offset / (m_numItems - m_pageSize); + float nibPos = (GetWidth() - nibSize) * percent; + if (nibPos < 0) nibPos = 0; + if (nibPos > GetWidth() - nibSize) nibPos = GetWidth() - nibSize; + m_guiBarNoFocus.SetPosition(GetXPosition() + nibPos, GetYPosition()); + m_guiBarFocus.SetPosition(GetXPosition() + nibPos, GetYPosition()); + m_guiNibNoFocus.SetPosition(GetXPosition() + nibPos, GetYPosition()); + m_guiNibFocus.SetPosition(GetXPosition() + nibPos, GetYPosition()); + } +} + +bool CGUIScrollBar::HitTest(const CPoint &point) const +{ + if (m_guiBackground.HitTest(point)) return true; + if (m_guiBarNoFocus.HitTest(point)) return true; + return false; +} + +void CGUIScrollBar::SetFromPosition(const CPoint &point) +{ + float fPercent; + if (m_orientation == VERTICAL) + fPercent = (point.y - m_guiBackground.GetYPosition() - 0.5f*m_guiBarFocus.GetHeight()) / m_guiBackground.GetHeight(); + else + fPercent = (point.x - m_guiBackground.GetXPosition() - 0.5f*m_guiBarFocus.GetWidth()) / m_guiBackground.GetWidth(); + if (fPercent < 0) fPercent = 0; + if (fPercent > 1) fPercent = 1; + m_offset = (int)(floor(fPercent * m_numItems + 0.5f)); + CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetParentID(), GetID(), GUI_MSG_PAGE_CHANGE, m_offset); + SendWindowMessage(message); + SetInvalid(); +} + +bool CGUIScrollBar::OnMouseClick(int button, const CPoint &point) +{ + g_Mouse.SetState(MOUSE_STATE_CLICK); + // turn off any exclusive access, if it's on... + g_Mouse.EndExclusiveAccess(GetID(), GetParentID()); + if (m_guiBackground.HitTest(point)) + { // set the position + SetFromPosition(point); + return true; + } + return false; +} + +bool CGUIScrollBar::OnMouseDrag(const CPoint &offset, const CPoint &point) +{ + g_Mouse.SetState(MOUSE_STATE_DRAG); + // get exclusive access to the mouse + g_Mouse.SetExclusiveAccess(GetID(), GetParentID(), point); + // get the position of the mouse + SetFromPosition(point); + return true; +} + +bool CGUIScrollBar::OnMouseWheel(char wheel, const CPoint &point) +{ + Move(-wheel); + return true; +} + +CStdString CGUIScrollBar::GetDescription() const +{ + CStdString description; + description.Format("%i/%i", m_offset, m_numItems); + return description; +} + +void CGUIScrollBar::UpdateColors() +{ + CGUIControl::UpdateColors(); + m_guiBackground.SetDiffuseColor(m_diffuseColor); + m_guiBarNoFocus.SetDiffuseColor(m_diffuseColor); + m_guiBarFocus.SetDiffuseColor(m_diffuseColor); + m_guiNibNoFocus.SetDiffuseColor(m_diffuseColor); + m_guiNibFocus.SetDiffuseColor(m_diffuseColor); +} + +bool CGUIScrollBar::IsVisible() const +{ + // page controls can be optionally disabled if the number of pages is 1 + if (m_numItems <= m_pageSize && !m_showOnePage) + return false; + return CGUIControl::IsVisible(); +} diff --git a/guilib/GUIScrollBarControl.h b/guilib/GUIScrollBarControl.h new file mode 100644 index 0000000000..9e98f96492 --- /dev/null +++ b/guilib/GUIScrollBarControl.h @@ -0,0 +1,86 @@ +/*! +\file GUIScrollBar.h +\brief +*/ + +#ifndef GUILIB_GUISCROLLBAR_H +#define GUILIB_GUISCROLLBAR_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUITexture.h" +#include "GUIControl.h" + +/*! + \ingroup controls + \brief + */ +class CGUIScrollBar : + public CGUIControl +{ +public: + CGUIScrollBar(int parentID, int controlID, float posX, float posY, + float width, float height, + const CTextureInfo& backGroundTexture, + const CTextureInfo& barTexture, const CTextureInfo& barTextureFocus, + const CTextureInfo& nibTexture, const CTextureInfo& nibTextureFocus, + ORIENTATION orientation, bool showOnePage); + virtual ~CGUIScrollBar(void); + virtual CGUIScrollBar *Clone() const { return new CGUIScrollBar(*this); }; + + virtual void Render(); + virtual bool OnAction(const CAction &action); + virtual void AllocResources(); + virtual void FreeResources(); + virtual void DynamicResourceAlloc(bool bOnOff); + virtual void SetRange(int pageSize, int numItems); + virtual bool OnMessage(CGUIMessage& message); + void SetValue(int value); + int GetValue() const; + virtual bool HitTest(const CPoint &point) const; + virtual bool OnMouseClick(int button, const CPoint &point); + virtual bool OnMouseDrag(const CPoint &offset, const CPoint &point); + virtual bool OnMouseWheel(char wheel, const CPoint &point); + virtual CStdString GetDescription() const; + virtual bool IsVisible() const; +protected: + virtual void UpdateColors(); + void UpdateBarSize(); + virtual void Move(int iNumSteps); + virtual void SetFromPosition(const CPoint &point); + + CGUITexture m_guiBackground; + CGUITexture m_guiBarNoFocus; + CGUITexture m_guiBarFocus; + CGUITexture m_guiNibNoFocus; + CGUITexture m_guiNibFocus; + + int m_numItems; + int m_pageSize; + int m_offset; + + bool m_showOnePage; + ORIENTATION m_orientation; +}; +#endif diff --git a/guilib/GUISelectButtonControl.cpp b/guilib/GUISelectButtonControl.cpp new file mode 100644 index 0000000000..a51d30e6d9 --- /dev/null +++ b/guilib/GUISelectButtonControl.cpp @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUISelectButtonControl.h" +#include "GUIWindowManager.h" +#include "utils/CharsetConverter.h" +#include "MouseStat.h" +#include "Key.h" + +CGUISelectButtonControl::CGUISelectButtonControl(int parentID, int controlID, + float posX, float posY, + float width, float height, + const CTextureInfo& buttonFocus, + const CTextureInfo& button, + const CLabelInfo& labelInfo, + const CTextureInfo& selectBackground, + const CTextureInfo& selectArrowLeft, + const CTextureInfo& selectArrowLeftFocus, + const CTextureInfo& selectArrowRight, + const CTextureInfo& selectArrowRightFocus + ) + : CGUIButtonControl(parentID, controlID, posX, posY, width, height, buttonFocus, button, labelInfo) + , m_imgBackground(posX, posY, width, height, selectBackground) + , m_imgLeft(posX, posY, 16, 16, selectArrowLeft) + , m_imgLeftFocus(posX, posY, 16, 16, selectArrowLeftFocus) + , m_imgRight(posX, posY, 16, 16, selectArrowRight) + , m_imgRightFocus(posX, posY, 16, 16, selectArrowRightFocus) +{ + m_bShowSelect = false; + m_iCurrentItem = -1; + m_iDefaultItem = -1; + m_iStartFrame = 0; + m_bLeftSelected = false; + m_bRightSelected = false; + m_bMovedLeft = false; + m_bMovedRight = false; + m_dwTicks = 0; + ControlType = GUICONTROL_SELECTBUTTON; +} + +CGUISelectButtonControl::~CGUISelectButtonControl(void) +{} + +void CGUISelectButtonControl::Render() +{ + if (m_bInvalidated) + { + m_imgBackground.SetWidth(m_width); + m_imgBackground.SetHeight(m_height); + } + // Are we in selection mode + if (m_bShowSelect) + { + // render background, left and right arrow + m_imgBackground.Render(); + + color_t textColor = m_label.textColor; + + // User has moved left... + if (m_bMovedLeft) + { + m_iStartFrame++; + if (m_iStartFrame >= 10) + { + m_iStartFrame = 0; + m_bMovedLeft = false; + } + // If we are moving left + // render item text as disabled + textColor = m_label.disabledColor; + } + + // Render arrow + if (m_bLeftSelected || m_bMovedLeft) + m_imgLeftFocus.Render(); + else + m_imgLeft.Render(); + + // User has moved right... + if (m_bMovedRight) + { + m_iStartFrame++; + if (m_iStartFrame >= 10) + { + m_iStartFrame = 0; + m_bMovedRight = false; + } + // If we are moving right + // render item text as disabled + textColor = m_label.disabledColor; + } + + // Render arrow + if (m_bRightSelected || m_bMovedRight) + m_imgRightFocus.Render(); + else + m_imgRight.Render(); + + // Render text if a current item is available + if (m_iCurrentItem >= 0 && (unsigned)m_iCurrentItem < m_vecItems.size()) + { + m_textLayout.Update(m_vecItems[m_iCurrentItem]); + uint32_t align = m_label.align | XBFONT_CENTER_X; + float fPosY = m_posY + m_label.offsetY; + if (m_label.align & XBFONT_CENTER_Y) + fPosY = m_posY + m_imgBackground.GetHeight()*0.5f; + m_textLayout.Render(m_posX + GetWidth()*0.5f, fPosY, 0, textColor, m_label.shadowColor, align, m_label.width); + } + + // Select current item, if user doesn't + // move left or right for 1.5 sec. + DWORD dwTicksSpan = timeGetTime() - m_dwTicks; + if (dwTicksSpan > 1500) + { + // User hasn't moved disable selection mode... + m_bShowSelect = false; + + // ...and send a thread message. + // (Sending a message with SendMessage + // can result in a GPF.) + CGUIMessage message(GUI_MSG_CLICKED, GetID(), GetParentID() ); + m_gWindowManager.SendThreadMessage(message); + } + } // if (m_bShowSelect) + else + { + // No, render a normal button + CGUIButtonControl::Render(); + } + CGUIControl::Render(); +} + +bool CGUISelectButtonControl::OnMessage(CGUIMessage& message) +{ + if ( message.GetControlId() == GetID() ) + { + if (message.GetMessage() == GUI_MSG_LABEL_ADD) + { + if (m_vecItems.size() <= 0) + { + m_iCurrentItem = 0; + m_iDefaultItem = 0; + } + m_vecItems.push_back(message.GetLabel()); + return true; + } + else if (message.GetMessage() == GUI_MSG_LABEL_RESET) + { + m_vecItems.erase(m_vecItems.begin(), m_vecItems.end()); + m_iCurrentItem = -1; + m_iDefaultItem = -1; + return true; + } + else if (message.GetMessage() == GUI_MSG_ITEM_SELECTED) + { + message.SetParam1(m_iCurrentItem); + if (m_iCurrentItem >= 0 && m_iCurrentItem < (int)m_vecItems.size()) + message.SetLabel(m_vecItems[m_iCurrentItem]); + return true; + } + else if (message.GetMessage() == GUI_MSG_ITEM_SELECT) + { + m_iDefaultItem = m_iCurrentItem = message.GetParam1(); + return true; + } + } + + return CGUIButtonControl::OnMessage(message); +} + +bool CGUISelectButtonControl::OnAction(const CAction &action) +{ + if (!m_bShowSelect) + { + if (action.id == ACTION_SELECT_ITEM) + { + // Enter selection mode + m_bShowSelect = true; + + // Start timer, if user doesn't select an item + // or moves left/right. The control will + // automatically select the current item. + m_dwTicks = timeGetTime(); + return true; + } + else + return CGUIButtonControl::OnAction(action); + } + else + { + if (action.id == ACTION_SELECT_ITEM) + { + // User has selected an item, disable selection mode... + m_bShowSelect = false; + + // ...and send a message. + CGUIMessage message(GUI_MSG_CLICKED, GetID(), GetParentID() ); + SendWindowMessage(message); + return true; + } + if (action.id == ACTION_MOVE_UP || action.id == ACTION_MOVE_DOWN ) + { + // Disable selection mode when moving up or down + m_bShowSelect = false; + m_iCurrentItem = m_iDefaultItem; + } + // call the base class + return CGUIButtonControl::OnAction(action); + } +} + +void CGUISelectButtonControl::FreeResources() +{ + CGUIButtonControl::FreeResources(); + + m_imgBackground.FreeResources(); + + m_imgLeft.FreeResources(); + m_imgLeftFocus.FreeResources(); + + m_imgRight.FreeResources(); + m_imgRightFocus.FreeResources(); + + m_bShowSelect = false; +} + +void CGUISelectButtonControl::DynamicResourceAlloc(bool bOnOff) +{ + CGUIControl::DynamicResourceAlloc(bOnOff); + + m_imgBackground.DynamicResourceAlloc(bOnOff); + + m_imgLeft.DynamicResourceAlloc(bOnOff); + m_imgLeftFocus.DynamicResourceAlloc(bOnOff); + + m_imgRight.DynamicResourceAlloc(bOnOff); + m_imgRightFocus.DynamicResourceAlloc(bOnOff); +} + +void CGUISelectButtonControl::AllocResources() +{ + CGUIButtonControl::AllocResources(); + + m_imgBackground.AllocResources(); + + m_imgLeft.AllocResources(); + m_imgLeftFocus.AllocResources(); + + m_imgRight.AllocResources(); + m_imgRightFocus.AllocResources(); + + // Position right arrow + float posX = (m_posX + m_width - 8) - 16; + float posY = m_posY + (m_height - 16) / 2; + m_imgRight.SetPosition(posX, posY); + m_imgRightFocus.SetPosition(posX, posY); + + // Position left arrow + posX = m_posX + 8; + m_imgLeft.SetPosition(posX, posY); + m_imgLeftFocus.SetPosition(posX, posY); +} + +void CGUISelectButtonControl::OnLeft() +{ + if (m_bShowSelect) + { + // Set for visual feedback + m_bMovedLeft = true; + m_iStartFrame = 0; + + // Reset timer for automatically selecting + // the current item. + m_dwTicks = timeGetTime(); + + // Switch to previous item + if (m_vecItems.size() > 0) + { + m_iCurrentItem--; + if (m_iCurrentItem < 0) + m_iCurrentItem = (int)m_vecItems.size() - 1; + } + } + else + { // use the base class + CGUIButtonControl::OnLeft(); + } +} + +void CGUISelectButtonControl::OnRight() +{ + if (m_bShowSelect) + { + // Set for visual feedback + m_bMovedRight = true; + m_iStartFrame = 0; + + // Reset timer for automatically selecting + // the current item. + m_dwTicks = timeGetTime(); + + // Switch to next item + if (m_vecItems.size() > 0) + { + m_iCurrentItem++; + if (m_iCurrentItem >= (int)m_vecItems.size()) + m_iCurrentItem = 0; + } + } + else + { // use the base class + CGUIButtonControl::OnRight(); + } +} + +bool CGUISelectButtonControl::OnMouseOver(const CPoint &point) +{ + bool ret = CGUIControl::OnMouseOver(point); + m_bLeftSelected = false; + m_bRightSelected = false; + if (m_imgLeft.HitTest(point)) + { // highlight the left control, but don't start moving until we have clicked + m_bLeftSelected = true; + } + if (m_imgRight.HitTest(point)) + { // highlight the right control, but don't start moving until we have clicked + m_bRightSelected = true; + } + // reset ticks + m_dwTicks = timeGetTime(); + return ret; +} + +bool CGUISelectButtonControl::OnMouseClick(int button, const CPoint &point) +{ // only left click handled + if (button != MOUSE_LEFT_BUTTON) return false; + if (m_bShowSelect && m_imgLeft.HitTest(point)) + { // move left + OnLeft(); + } + else if (m_bShowSelect && m_imgRight.HitTest(point)) + { // move right + OnRight(); + } + else + { // normal select + CGUIButtonControl::OnMouseClick(button, point); + } + return true; +} + +bool CGUISelectButtonControl::OnMouseWheel(char wheel, const CPoint &point) +{ + if (wheel > 0) + OnLeft(); + else + OnRight(); + return true; +} + +void CGUISelectButtonControl::SetPosition(float posX, float posY) +{ + float leftOffX = m_imgLeft.GetXPosition() - m_posX; + float leftOffY = m_imgLeft.GetYPosition() - m_posY; + float rightOffX = m_imgRight.GetXPosition() - m_posX; + float rightOffY = m_imgRight.GetYPosition() - m_posY; + float backOffX = m_imgBackground.GetXPosition() - m_posX; + float backOffY = m_imgBackground.GetYPosition() - m_posY; + CGUIButtonControl::SetPosition(posX, posY); + m_imgLeft.SetPosition(posX + leftOffX, posY + leftOffY); + m_imgLeftFocus.SetPosition(posX + leftOffX, posY + leftOffY); + m_imgRight.SetPosition(posX + rightOffX, posY + rightOffY); + m_imgRightFocus.SetPosition(posX + rightOffX, posY + rightOffY); + m_imgBackground.SetPosition(posX + backOffX, posY + backOffY); +} + +void CGUISelectButtonControl::UpdateColors() +{ + CGUIButtonControl::UpdateColors(); + m_imgLeft.SetDiffuseColor(m_diffuseColor); + m_imgLeftFocus.SetDiffuseColor(m_diffuseColor); + m_imgRight.SetDiffuseColor(m_diffuseColor); + m_imgRightFocus.SetDiffuseColor(m_diffuseColor); + m_imgBackground.SetDiffuseColor(m_diffuseColor); +} + diff --git a/guilib/GUISelectButtonControl.h b/guilib/GUISelectButtonControl.h new file mode 100644 index 0000000000..8ca08418f7 --- /dev/null +++ b/guilib/GUISelectButtonControl.h @@ -0,0 +1,134 @@ +/*! +\file GUISelectButtonControl.h +\brief +*/ + +#ifndef GUILIB_GUIWINDOWSELECTCONTROL_H +#define GUILIB_GUIWINDOWSELECTCONTROL_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIButtonControl.h" + +/*! + \ingroup controls + \brief Button with multi selection choice. + + Behaves like a normal button control, but when pressing, + it can show multiple strings. The user can choose one by + moving left or right. \n + \n + Messages the button reactes on: \n + + - GUI_MSG_LABEL_ADD \n + Add a label to the control. Use CGUIMessage::SetLabel + to set the label text. + - GUI_MSG_LABEL_RESET \n + Remove all labels from the control. + - GUI_MSG_ITEM_SELECTED \n + After sending this message the CGUIMessage::GetParam1 + contains the selected label as an integer. + \note The order of the items depends on the order they have been added to + the control using GUI_MSG_LABEL_ADD. + - GUI_MSG_ITEM_SELECT \n + Send this message with CGUIMessage::SetParam1() set to the label + to be selected. \n + \n + Example entry to define a select button in a window or as reference control: \n + \verbatim + <control> + <description>default select button</description + <type>selectbutton</type> + <id>6</id> + <posX>60</posX> + <posY>192</posY> + <width>130</width> + <height>32</height> + <label>132</label> + <font>font13</font> + <textureFocus>button-focus.png</textureFocus> + <textureNoFocus>button-nofocus.jpg</textureNoFocus> + <texturebg>button-focus.png</texturebg> + <textureLeft>scroll-left.png</textureLeft> + <textureRight>scroll-right.png</textureRight> + <font>font13</font> + <textcolor>ffffffff</textcolor> + <colordiffuse>ffffffff</colordiffuse> + <disabledcolor>60ffffff</disabledcolor> + <onleft>50</onleft> + <onright>50</onright> + <onup>3</onup> + <ondown>7</ondown> + </control> + \endverbatim + + \sa CGUIMessage + */ +class CGUISelectButtonControl : public CGUIButtonControl +{ +public: + CGUISelectButtonControl(int parentID, int controlID, + float posX, float posY, + float width, float height, + const CTextureInfo& buttonFocus, const CTextureInfo& button, + const CLabelInfo& labelInfo, + const CTextureInfo& selectBackground, + const CTextureInfo& selectArrowLeft, const CTextureInfo& selectArrowLeftFocus, + const CTextureInfo& selectArrowRight, const CTextureInfo& selectArrowRightFocus); + virtual ~CGUISelectButtonControl(void); + virtual CGUISelectButtonControl *Clone() const { return new CGUISelectButtonControl(*this); }; + + virtual void Render(); + virtual bool OnAction(const CAction &action) ; + virtual void OnLeft(); + virtual void OnRight(); + virtual bool OnMessage(CGUIMessage& message); + virtual bool OnMouseOver(const CPoint &point); + virtual bool OnMouseClick(int button, const CPoint &point); + virtual bool OnMouseWheel(char wheel, const CPoint &point); + + virtual void AllocResources(); + virtual void FreeResources(); + virtual void DynamicResourceAlloc(bool bOnOff); + virtual void SetPosition(float posX, float posY); + +protected: + virtual void UpdateColors(); + bool m_bShowSelect; + CGUITexture m_imgBackground; + CGUITexture m_imgLeft; + CGUITexture m_imgLeftFocus; + CGUITexture m_imgRight; + CGUITexture m_imgRightFocus; + std::vector<std::string> m_vecItems; + int m_iCurrentItem; + int m_iDefaultItem; + int m_iStartFrame; + bool m_bLeftSelected; + bool m_bRightSelected; + bool m_bMovedLeft; + bool m_bMovedRight; + DWORD m_dwTicks; +}; +#endif diff --git a/guilib/GUISettingsSliderControl.cpp b/guilib/GUISettingsSliderControl.cpp new file mode 100644 index 0000000000..c6f0e3206a --- /dev/null +++ b/guilib/GUISettingsSliderControl.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUISettingsSliderControl.h" + +CGUISettingsSliderControl::CGUISettingsSliderControl(int parentID, int controlID, float posX, float posY, float width, float height, float sliderWidth, float sliderHeight, const CTextureInfo &textureFocus, const CTextureInfo &textureNoFocus, const CTextureInfo& backGroundTexture, const CTextureInfo& nibTexture, const CTextureInfo& nibTextureFocus, const CLabelInfo &labelInfo, int iType) + : CGUISliderControl(parentID, controlID, posX, posY, sliderWidth, sliderHeight, backGroundTexture, nibTexture,nibTextureFocus, iType) + , m_buttonControl(parentID, controlID, posX, posY, width, height, textureFocus, textureNoFocus, labelInfo) + , m_textLayout(labelInfo.font, false) +{ + ControlType = GUICONTROL_SETTINGS_SLIDER; +} + +CGUISettingsSliderControl::~CGUISettingsSliderControl(void) +{ +} + + +void CGUISettingsSliderControl::Render() +{ + if (IsDisabled()) return ; + + // make sure the button has focus if it should have... + m_buttonControl.SetFocus(HasFocus()); + m_buttonControl.SetPulseOnSelect(m_pulseOnSelect); + m_buttonControl.Render(); + CGUISliderControl::Render(); + + // now render our text + m_textLayout.Update(CGUISliderControl::GetDescription()); + + float posX = m_posX - m_buttonControl.GetLabelInfo().offsetX; + float posY = GetYPosition() + GetHeight() * 0.5f; + if (HasFocus() && m_buttonControl.GetLabelInfo().focusedColor) + m_textLayout.Render(posX, posY, 0, m_buttonControl.GetLabelInfo().focusedColor, m_buttonControl.GetLabelInfo().shadowColor, XBFONT_CENTER_Y | XBFONT_RIGHT, 0); + else + m_textLayout.Render(posX, posY, 0, m_buttonControl.GetLabelInfo().textColor, m_buttonControl.GetLabelInfo().shadowColor, XBFONT_CENTER_Y | XBFONT_RIGHT, 0); +} + +bool CGUISettingsSliderControl::OnAction(const CAction &action) +{ + return CGUISliderControl::OnAction(action); +} + +void CGUISettingsSliderControl::FreeResources() +{ + CGUISliderControl::FreeResources(); + m_buttonControl.FreeResources(); +} + +void CGUISettingsSliderControl::DynamicResourceAlloc(bool bOnOff) +{ + CGUISliderControl::DynamicResourceAlloc(bOnOff); + m_buttonControl.DynamicResourceAlloc(bOnOff); +} + +void CGUISettingsSliderControl::AllocResources() +{ + CGUISliderControl::AllocResources(); + m_buttonControl.AllocResources(); +} + +void CGUISettingsSliderControl::SetPosition(float posX, float posY) +{ + m_buttonControl.SetPosition(posX, posY); + float sliderPosX = posX + m_buttonControl.GetWidth() - m_width - m_buttonControl.GetLabelInfo().offsetX; + float sliderPosY = posY + (m_buttonControl.GetHeight() - m_height) * 0.5f; + CGUISliderControl::SetPosition(sliderPosX, sliderPosY); +} + +void CGUISettingsSliderControl::SetWidth(float width) +{ + m_buttonControl.SetWidth(width); + SetPosition(GetXPosition(), GetYPosition()); +} + +void CGUISettingsSliderControl::SetHeight(float height) +{ + m_buttonControl.SetHeight(height); + SetPosition(GetXPosition(), GetYPosition()); +} + +void CGUISettingsSliderControl::SetEnabled(bool bEnable) +{ + CGUISliderControl::SetEnabled(bEnable); + m_buttonControl.SetEnabled(bEnable); +} + +CStdString CGUISettingsSliderControl::GetDescription() const +{ + return m_buttonControl.GetDescription() + " " + CGUISliderControl::GetDescription(); +} + +void CGUISettingsSliderControl::UpdateColors() +{ + m_buttonControl.UpdateColors(); + CGUISliderControl::UpdateColors(); +} diff --git a/guilib/GUISettingsSliderControl.h b/guilib/GUISettingsSliderControl.h new file mode 100644 index 0000000000..eb9a5a6f6e --- /dev/null +++ b/guilib/GUISettingsSliderControl.h @@ -0,0 +1,73 @@ +/*! +\file GUISliderControl.h +\brief +*/ + +#ifndef GUILIB_GUISettingsSliderCONTROL_H +#define GUILIB_GUISettingsSliderCONTROL_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUISliderControl.h" +#include "GUIButtonControl.h" + +#define SPIN_CONTROL_TYPE_INT 1 +#define SPIN_CONTROL_TYPE_FLOAT 2 +#define SPIN_CONTROL_TYPE_TEXT 3 + +/*! + \ingroup controls + \brief + */ +class CGUISettingsSliderControl : + public CGUISliderControl +{ +public: + CGUISettingsSliderControl(int parentID, int controlID, float posX, float posY, float width, float height, float sliderWidth, float sliderHeight, const CTextureInfo &textureFocus, const CTextureInfo &textureNoFocus, const CTextureInfo& backGroundTexture, const CTextureInfo& nibTexture, const CTextureInfo& nibTextureFocus, const CLabelInfo &labelInfo, int iType); + virtual ~CGUISettingsSliderControl(void); + virtual CGUISettingsSliderControl *Clone() const { return new CGUISettingsSliderControl(*this); }; + + virtual void Render(); + virtual bool OnAction(const CAction &action); + virtual void AllocResources(); + virtual void FreeResources(); + virtual void DynamicResourceAlloc(bool bOnOff); + virtual void SetPosition(float posX, float posY); + virtual float GetWidth() const { return m_buttonControl.GetWidth();}; + virtual void SetWidth(float width); + virtual float GetHeight() const { return m_buttonControl.GetHeight();}; + virtual void SetHeight(float height); + virtual void SetEnabled(bool bEnable); + + void SetText(const std::string &label) {m_buttonControl.SetLabel(label);}; + virtual float GetXPosition() const { return m_buttonControl.GetXPosition();}; + virtual float GetYPosition() const { return m_buttonControl.GetYPosition();}; + virtual CStdString GetDescription() const; + virtual bool HitTest(const CPoint &point) const { return m_buttonControl.HitTest(point); }; +protected: + virtual void UpdateColors(); + CGUIButtonControl m_buttonControl; + CGUITextLayout m_textLayout; +}; +#endif diff --git a/guilib/GUISliderControl.cpp b/guilib/GUISliderControl.cpp new file mode 100644 index 0000000000..582a195c84 --- /dev/null +++ b/guilib/GUISliderControl.cpp @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUISliderControl.h" +#include "utils/GUIInfoManager.h" +#include "MouseStat.h" +#include "Key.h" + +CGUISliderControl::CGUISliderControl(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& backGroundTexture, const CTextureInfo& nibTexture, const CTextureInfo& nibTextureFocus, int iType) + : CGUIControl(parentID, controlID, posX, posY, width, height) + , m_guiBackground(posX, posY, width, height, backGroundTexture) + , m_guiMid(posX, posY, width, height, nibTexture) + , m_guiMidFocus(posX, posY, width, height, nibTextureFocus) +{ + m_iType = iType; + m_iPercent = 0; + m_iStart = 0; + m_iEnd = 100; + m_fStart = 0.0f; + m_fEnd = 1.0f; + m_fInterval = 0.1f; + m_iValue = 0; + m_fValue = 0.0; + ControlType = GUICONTROL_SLIDER; + m_iInfoCode = 0; +} + +CGUISliderControl::~CGUISliderControl(void) +{ +} + +void CGUISliderControl::Render() +{ + m_guiBackground.SetPosition( m_posX, m_posY ); + float proportion = 0; + if (!IsDisabled()) + { + switch (m_iType) + { + case SPIN_CONTROL_TYPE_FLOAT: + if (m_iInfoCode) m_fValue = (float)g_infoManager.GetInt(m_iInfoCode); + + proportion = (m_fValue - m_fStart) / (m_fEnd - m_fStart); + break; + + case SPIN_CONTROL_TYPE_INT: + if (m_iInfoCode) m_iValue = g_infoManager.GetInt(m_iInfoCode); + + proportion = (float)(m_iValue - m_iStart) / (float)(m_iEnd - m_iStart); + break; + default: + if (m_iInfoCode) m_iPercent = g_infoManager.GetInt(m_iInfoCode); + proportion = 0.01f * m_iPercent; + break; + } + + float fScaleX = m_width == 0 ? 1.0f : m_width / m_guiBackground.GetTextureWidth(); + float fScaleY = m_height == 0 ? 1.0f : m_height / m_guiBackground.GetTextureHeight(); + + m_guiBackground.SetHeight(m_height); + m_guiBackground.SetWidth(m_width); + m_guiBackground.Render(); + + float fWidth = (m_guiBackground.GetTextureWidth() - m_guiMid.GetTextureWidth())*fScaleX; + + float fPos = m_guiBackground.GetXPosition() + proportion * fWidth; + + if ((int)fWidth > 1) + { + if (m_bHasFocus) + { + m_guiMidFocus.SetPosition(fPos, m_guiBackground.GetYPosition() ); + m_guiMidFocus.SetWidth(m_guiMidFocus.GetTextureWidth() * fScaleX); + m_guiMidFocus.SetHeight(m_guiMidFocus.GetTextureHeight() * fScaleY); + m_guiMidFocus.Render(); + } + else + { + m_guiMid.SetPosition(fPos, m_guiBackground.GetYPosition() ); + m_guiMid.SetWidth(m_guiMid.GetTextureWidth()*fScaleX); + m_guiMid.SetHeight(m_guiMid.GetTextureHeight()*fScaleY); + m_guiMid.Render(); + } + } + } + CGUIControl::Render(); +} + +bool CGUISliderControl::OnMessage(CGUIMessage& message) +{ + if (message.GetControlId() == GetID() ) + { + switch (message.GetMessage()) + { + case GUI_MSG_ITEM_SELECT: + SetPercentage( message.GetParam1() ); + return true; + break; + + case GUI_MSG_LABEL_RESET: + { + SetPercentage(0); + return true; + } + break; + } + } + + return CGUIControl::OnMessage(message); +} + +bool CGUISliderControl::OnAction(const CAction &action) +{ + switch ( action.id ) + { + case ACTION_MOVE_LEFT: + //case ACTION_OSD_SHOW_VALUE_MIN: + Move( -1); + return true; + break; + + case ACTION_MOVE_RIGHT: + //case ACTION_OSD_SHOW_VALUE_PLUS: + Move(1); + return true; + break; + default: + return CGUIControl::OnAction(action); + } +} + +void CGUISliderControl::Move(int iNumSteps) +{ + switch (m_iType) + { + case SPIN_CONTROL_TYPE_FLOAT: + m_fValue += m_fInterval * iNumSteps; + if (m_fValue < m_fStart) m_fValue = m_fStart; + if (m_fValue > m_fEnd) m_fValue = m_fEnd; + break; + + case SPIN_CONTROL_TYPE_INT: + m_iValue += iNumSteps; + if (m_iValue < m_iStart) m_iValue = m_iStart; + if (m_iValue > m_iEnd) m_iValue = m_iEnd; + break; + + default: + m_iPercent += iNumSteps; + if (m_iPercent < 0) m_iPercent = 0; + if (m_iPercent > 100) m_iPercent = 100; + break; + } + SEND_CLICK_MESSAGE(GetID(), GetParentID(), 0); +} + +void CGUISliderControl::SetPercentage(int iPercent) +{ + if (iPercent > 100) iPercent = 100; + if (iPercent < 0) iPercent = 0; + m_iPercent = iPercent; +} + +int CGUISliderControl::GetPercentage() const +{ + return m_iPercent; +} + +void CGUISliderControl::SetIntValue(int iValue) +{ + if (m_iType == SPIN_CONTROL_TYPE_FLOAT) + m_fValue = (float)iValue; + else if (m_iType == SPIN_CONTROL_TYPE_INT) + m_iValue = iValue; + else + SetPercentage(iValue); +} + +int CGUISliderControl::GetIntValue() const +{ + if (m_iType == SPIN_CONTROL_TYPE_FLOAT) + return (int)m_fValue; + else if (m_iType == SPIN_CONTROL_TYPE_INT) + return m_iValue; + else + return m_iPercent; +} + +void CGUISliderControl::SetFloatValue(float fValue) +{ + if (m_iType == SPIN_CONTROL_TYPE_FLOAT) + m_fValue = fValue; + else if (m_iType == SPIN_CONTROL_TYPE_INT) + m_iValue = (int)fValue; + else + SetPercentage((int)fValue); +} + +float CGUISliderControl::GetFloatValue() const +{ + if (m_iType == SPIN_CONTROL_TYPE_FLOAT) + return m_fValue; + else if (m_iType == SPIN_CONTROL_TYPE_INT) + return (float)m_iValue; + else + return (float)m_iPercent; +} + +void CGUISliderControl::SetFloatInterval(float fInterval) +{ + m_fInterval = fInterval; +} + +void CGUISliderControl::SetRange(int iStart, int iEnd) +{ + if (m_iType == SPIN_CONTROL_TYPE_FLOAT) + SetFloatRange((float)iStart,(float)iEnd); + else + { + m_iStart = iStart; + m_iEnd = iEnd; + } +} + +void CGUISliderControl::SetFloatRange(float fStart, float fEnd) +{ + if (m_iType == SPIN_CONTROL_TYPE_INT) + SetRange((int)fStart, (int)fEnd); + else + { + m_fStart = fStart; + m_fEnd = fEnd; + } +} + +void CGUISliderControl::FreeResources() +{ + CGUIControl::FreeResources(); + m_guiBackground.FreeResources(); + m_guiMid.FreeResources(); + m_guiMidFocus.FreeResources(); +} + +void CGUISliderControl::DynamicResourceAlloc(bool bOnOff) +{ + CGUIControl::DynamicResourceAlloc(bOnOff); + m_guiBackground.DynamicResourceAlloc(bOnOff); + m_guiMid.DynamicResourceAlloc(bOnOff); + m_guiMidFocus.DynamicResourceAlloc(bOnOff); +} + +void CGUISliderControl::AllocResources() +{ + CGUIControl::AllocResources(); + m_guiBackground.AllocResources(); + m_guiMid.AllocResources(); + m_guiMidFocus.AllocResources(); +} + +bool CGUISliderControl::HitTest(const CPoint &point) const +{ + if (m_guiBackground.HitTest(point)) return true; + if (m_guiMid.HitTest(point)) return true; + return false; +} + +void CGUISliderControl::SetFromPosition(const CPoint &point) +{ + float fPercent = (point.x - m_guiBackground.GetXPosition()) / m_guiBackground.GetWidth(); + if (fPercent < 0) fPercent = 0; + if (fPercent > 1) fPercent = 1; + switch (m_iType) + { + case SPIN_CONTROL_TYPE_FLOAT: + m_fValue = m_fStart + (m_fEnd - m_fStart) * fPercent; + break; + + case SPIN_CONTROL_TYPE_INT: + m_iValue = (int)(m_iStart + (float)(m_iEnd - m_iStart) * fPercent + 0.49f); + break; + + default: + m_iPercent = (int)(fPercent * 100 + 0.49f); + break; + } + SEND_CLICK_MESSAGE(GetID(), GetParentID(), 0); +} + +bool CGUISliderControl::OnMouseClick(int button, const CPoint &point) +{ + g_Mouse.SetState(MOUSE_STATE_CLICK); + // turn off any exclusive access, if it's on... + g_Mouse.EndExclusiveAccess(GetID(), GetParentID()); + if (m_guiBackground.HitTest(point)) + { // set the position + SetFromPosition(point); + return true; + } + return false; +} + +bool CGUISliderControl::OnMouseDrag(const CPoint &offset, const CPoint &point) +{ + g_Mouse.SetState(MOUSE_STATE_DRAG); + // get exclusive access to the mouse + g_Mouse.SetExclusiveAccess(GetID(), GetParentID(), point); + // get the position of the mouse + SetFromPosition(point); + return true; +} + +bool CGUISliderControl::OnMouseWheel(char wheel, const CPoint &point) +{ // move the slider 10 steps in the appropriate direction + Move(wheel*10); + return true; +} + +void CGUISliderControl::SetInfo(int iInfo) +{ + m_iInfoCode = iInfo; +} + +CStdString CGUISliderControl::GetDescription() const +{ + if (!m_textValue.IsEmpty()) + return m_textValue; + CStdString description; + if (m_iType == SPIN_CONTROL_TYPE_FLOAT) + description.Format("%2.2f", m_fValue); + else if (m_iType == SPIN_CONTROL_TYPE_INT) + description.Format("%i", m_iValue); + else + description.Format("%i%%", m_iPercent); + return description; +} + +void CGUISliderControl::UpdateColors() +{ + CGUIControl::UpdateColors(); + m_guiBackground.SetDiffuseColor(m_diffuseColor); + m_guiMid.SetDiffuseColor(m_diffuseColor); + m_guiMidFocus.SetDiffuseColor(m_diffuseColor); +} + diff --git a/guilib/GUISliderControl.h b/guilib/GUISliderControl.h new file mode 100644 index 0000000000..cab440021c --- /dev/null +++ b/guilib/GUISliderControl.h @@ -0,0 +1,98 @@ +/*! +\file GUISliderControl.h +\brief +*/ + +#ifndef GUILIB_GUISLIDERCONTROL_H +#define GUILIB_GUISLIDERCONTROL_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControl.h" +#include "GUITexture.h" + +#define SPIN_CONTROL_TYPE_INT 1 +#define SPIN_CONTROL_TYPE_FLOAT 2 +#define SPIN_CONTROL_TYPE_TEXT 3 + +/*! + \ingroup controls + \brief + */ +class CGUISliderControl : + public CGUIControl +{ +public: + CGUISliderControl(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& backGroundTexture, const CTextureInfo& mibTexture, const CTextureInfo& nibTextureFocus, int iType); + virtual ~CGUISliderControl(void); + virtual CGUISliderControl *Clone() const { return new CGUISliderControl(*this); }; + + virtual void Render(); + virtual bool OnAction(const CAction &action); + virtual void AllocResources(); + virtual void FreeResources(); + virtual void DynamicResourceAlloc(bool bOnOff); + virtual void SetRange(int iStart, int iEnd); + virtual void SetFloatRange(float fStart, float fEnd); + virtual bool OnMessage(CGUIMessage& message); + void SetInfo(int iInfo); + void SetPercentage(int iPercent); + int GetPercentage() const; + void SetIntValue(int iValue); + int GetIntValue() const; + void SetFloatValue(float fValue); + float GetFloatValue() const; + void SetFloatInterval(float fInterval); + void SetType(int iType) { m_iType = iType; }; + virtual bool HitTest(const CPoint &point) const; + virtual bool OnMouseClick(int button, const CPoint &point); + virtual bool OnMouseDrag(const CPoint &offset, const CPoint &point); + virtual bool OnMouseWheel(char wheel, const CPoint &point); + virtual CStdString GetDescription() const; + void SetTextValue(const CStdString &textValue) { m_textValue = textValue; }; +protected: + virtual void UpdateColors(); + virtual void Move(int iNumSteps); + virtual void SetFromPosition(const CPoint &point); + + CGUITexture m_guiBackground; + CGUITexture m_guiMid; + CGUITexture m_guiMidFocus; + int m_iType; + + int m_iPercent; + + int m_iValue; + int m_iStart; + int m_iEnd; + + float m_fValue; + float m_fStart; + float m_fInterval; + float m_fEnd; + + int m_iInfoCode; + CStdString m_textValue; ///< Allows overriding of the text value to be displayed (parent must update when the slider updates) +}; +#endif diff --git a/guilib/GUISound.cpp b/guilib/GUISound.cpp new file mode 100644 index 0000000000..8d80aa6d8e --- /dev/null +++ b/guilib/GUISound.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" +#include "GUISound.h" +#include "AudioContext.h" +#include "Settings.h" +#include "FileSystem/File.h" +#include "utils/log.h" +#ifdef HAS_SDL_AUDIO +#include <SDL/SDL_mixer.h> +#include "FileSystem/SpecialProtocol.h" +#endif +#ifndef HAS_SDL_AUDIO + +typedef struct +{ + char chunk_id[4]; + long chunksize; +} WAVE_CHUNK; + +typedef struct +{ + char riff[4]; + long filesize; + char rifftype[4]; +} WAVE_RIFFHEADER; +#else +#define GUI_SOUND_CHANNEL 0 +#endif + +CGUISound::CGUISound() +{ + m_soundBuffer=NULL; +} + +CGUISound::~CGUISound() +{ +#ifdef _WIN32 + FreeBuffer(); +#elif defined(HAS_SDL_AUDIO) + Mix_FreeChunk(m_soundBuffer); +#endif +} + +// \brief Loads a wav file by filename +bool CGUISound::Load(const CStdString& strFile) +{ +#ifdef _WIN32 + LPBYTE pbData=NULL; + WAVEFORMATEX wfx; + int size=0; + if (!LoadWav(strFile, &wfx, &pbData, &size)) + return false; + + bool bReady=(CreateBuffer(&wfx, size) && FillBuffer(pbData, size)); + + if (!bReady) + FreeBuffer(); + + delete[] pbData; + + return bReady; +#elif defined(HAS_SDL_AUDIO) + m_soundBuffer = Mix_LoadWAV(_P(strFile)); + if (!m_soundBuffer) + return false; + + return true; +#else + return false; +#endif +} + +// \brief Starts playback of the sound +void CGUISound::Play() +{ + if (m_soundBuffer) + { +#ifdef _WIN32 + m_soundBuffer->Play(0, 0, 0); +#elif defined(HAS_SDL_AUDIO) + Mix_PlayChannel(GUI_SOUND_CHANNEL, m_soundBuffer, 0); +#endif + } +} + +// \brief returns true if the sound is playing +bool CGUISound::IsPlaying() +{ +#ifdef _WIN32 + if (m_soundBuffer) + { + DWORD dwStatus; + m_soundBuffer->GetStatus(&dwStatus); + return (dwStatus & DSBSTATUS_PLAYING); + } + + return false; +#elif defined(HAS_SDL_AUDIO) + return Mix_Playing(GUI_SOUND_CHANNEL) != 0; +#else + return false; +#endif +} + +// \brief Stops playback if the sound +void CGUISound::Stop() +{ + if (m_soundBuffer) + { +#ifdef _WIN32 + m_soundBuffer->Stop(); +#elif defined(HAS_SDL_AUDIO) + Mix_HaltChannel(GUI_SOUND_CHANNEL); +#endif + + while(IsPlaying()) {} + } +} + +// \brief Sets the volume of the sound +void CGUISound::SetVolume(int level) +{ + if (m_soundBuffer) + { +#ifdef _WIN32 + m_soundBuffer->SetVolume(level); +#elif defined(HAS_SDL_AUDIO) + Mix_Volume(GUI_SOUND_CHANNEL, level); +#endif + } +} + +#ifdef _WIN32 +bool CGUISound::CreateBuffer(LPWAVEFORMATEX wfx, int iLength) +{ + // Set up DSBUFFERDESC structure + DSBUFFERDESC dsbdesc; + memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); + dsbdesc.dwSize=sizeof(DSBUFFERDESC); + // directsound requires ctrlvolume to be set + dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME; + dsbdesc.dwBufferBytes=iLength; + dsbdesc.lpwfxFormat=wfx; + + LPDIRECTSOUND directSound=g_audioContext.GetDirectSoundDevice(); + if (!directSound) + return false; + + // Create buffer + if (FAILED(directSound->CreateSoundBuffer(&dsbdesc, &m_soundBuffer, NULL))) + { + m_soundBuffer = NULL; + CLog::Log(LOGERROR, __FUNCTION__" Creating sound buffer failed!"); + return false; + } + + // Make effects as loud as possible + m_soundBuffer->SetVolume(g_stSettings.m_nVolumeLevel); + + return true; +} + +bool CGUISound::FillBuffer(LPBYTE pbData, int iLength) +{ + if (!m_soundBuffer) + return false; + + LPVOID lpvWrite; + DWORD dwLength; + + if (SUCCEEDED(m_soundBuffer->Lock(0, 0, &lpvWrite, &dwLength, NULL, NULL, DSBLOCK_ENTIREBUFFER))) + { + memcpy(lpvWrite, pbData, iLength); + m_soundBuffer->Unlock(lpvWrite, dwLength, NULL, 0); + return true; + } + + CLog::Log(LOGERROR, __FUNCTION__" Filling sound buffer failed!"); + + return false; +} + +void CGUISound::FreeBuffer() +{ + if (IsPlaying()) + Stop(); + + SAFE_RELEASE(m_soundBuffer); +} + +bool CGUISound::LoadWav(const CStdString& strFile, WAVEFORMATEX* wfx, LPBYTE* ppWavData, int* pDataSize) +{ + XFILE::CFile file; + if (!file.Open(strFile)) + return false; + + // read header + WAVE_RIFFHEADER riffh; + file.Read(&riffh, sizeof(WAVE_RIFFHEADER)); + + // file valid? + if (strncmp(riffh.riff, "RIFF", 4)!=0 && strncmp(riffh.rifftype, "WAVE", 4)!=0) + { + file.Close(); + return false; + } + + long offset=0; + offset += sizeof(WAVE_RIFFHEADER); + offset -= sizeof(WAVE_CHUNK); + + // parse chunks + do + { + WAVE_CHUNK chunk; + + // always seeking to the start of a chunk + file.Seek(offset + sizeof(WAVE_CHUNK), SEEK_SET); + file.Read(&chunk, sizeof(WAVE_CHUNK)); + + if (!strncmp(chunk.chunk_id, "fmt ", 4)) + { // format chunk + memset(wfx, 0, sizeof(WAVEFORMATEX)); + file.Read(wfx, 16); + // we only need 16 bytes of the fmt chunk + if (chunk.chunksize-16>0) + file.Seek(chunk.chunksize-16, SEEK_CUR); + } + else if (!strncmp(chunk.chunk_id, "data", 4)) + { // data chunk + *ppWavData=new BYTE[chunk.chunksize+1]; + file.Read(*ppWavData, chunk.chunksize); + *pDataSize=chunk.chunksize; + + if (chunk.chunksize & 1) + offset++; + } + else + { // other chunk - unused, just skip + file.Seek(chunk.chunksize, SEEK_CUR); + } + + offset+=(chunk.chunksize+sizeof(WAVE_CHUNK)); + + if (offset & 1) + offset++; + + } while (offset+(int)sizeof(WAVE_CHUNK) < riffh.filesize); + + file.Close(); + return (*ppWavData!=NULL); +} + +#endif diff --git a/guilib/GUISound.h b/guilib/GUISound.h new file mode 100644 index 0000000000..3103afe1da --- /dev/null +++ b/guilib/GUISound.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +#include "StdString.h" + +#ifdef HAS_SDL_AUDIO +#include <SDL/SDL_mixer.h> +#endif + +class CGUISound +{ +public: + CGUISound(); + virtual ~CGUISound(); + + bool Load(const CStdString& strFile); + void Play(); + void Stop(); + bool IsPlaying(); + void SetVolume(int level); + +private: +#ifdef _WIN32 + bool LoadWav(const CStdString& strFile, WAVEFORMATEX* wfx, LPBYTE* ppWavData, int* pDataSize); + bool CreateBuffer(LPWAVEFORMATEX wfx, int iLength); + bool FillBuffer(LPBYTE pbData, int iLength); + void FreeBuffer(); + + LPDIRECTSOUNDBUFFER m_soundBuffer; +#elif defined(HAS_SDL_AUDIO) + Mix_Chunk* m_soundBuffer; +#else + void *m_soundBuffer; +#endif +}; diff --git a/guilib/GUISpinControl.cpp b/guilib/GUISpinControl.cpp new file mode 100644 index 0000000000..bca3947d56 --- /dev/null +++ b/guilib/GUISpinControl.cpp @@ -0,0 +1,939 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUISpinControl.h" +#include "utils/CharsetConverter.h" +#include "MouseStat.h" +#include "Key.h" + +using namespace std; + +#define SPIN_BUTTON_DOWN 1 +#define SPIN_BUTTON_UP 2 + +CGUISpinControl::CGUISpinControl(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& textureUp, const CTextureInfo& textureDown, const CTextureInfo& textureUpFocus, const CTextureInfo& textureDownFocus, const CLabelInfo &labelInfo, int iType) + : CGUIControl(parentID, controlID, posX, posY, width, height) + , m_imgspinUp(posX, posY, width, height, textureUp) + , m_imgspinDown(posX, posY, width, height, textureDown) + , m_imgspinUpFocus(posX, posY, width, height, textureUpFocus) + , m_imgspinDownFocus(posX, posY, width, height, textureDownFocus) + , m_textLayout(labelInfo.font, false) +{ + m_bReverse = false; + m_iStart = 0; + m_iEnd = 100; + m_fStart = 0.0f; + m_fEnd = 1.0f; + m_fInterval = 0.1f; + m_iValue = 0; + m_label = labelInfo; + m_label.align |= XBFONT_CENTER_Y; + m_fValue = 0.0; + m_iType = iType; + m_iSelect = SPIN_BUTTON_DOWN; + m_bShowRange = false; + m_iTypedPos = 0; + strcpy(m_szTyped, ""); + ControlType = GUICONTROL_SPIN; + m_currentItem = 0; + m_numItems = 10; + m_itemsPerPage = 10; + m_showOnePage = true; +} + +CGUISpinControl::~CGUISpinControl(void) +{} + +bool CGUISpinControl::OnAction(const CAction &action) +{ + switch (action.id) + { + case REMOTE_0: + case REMOTE_1: + case REMOTE_2: + case REMOTE_3: + case REMOTE_4: + case REMOTE_5: + case REMOTE_6: + case REMOTE_7: + case REMOTE_8: + case REMOTE_9: + { + if (strlen(m_szTyped) >= 3) + { + m_iTypedPos = 0; + strcpy(m_szTyped, ""); + } + int iNumber = action.id - REMOTE_0; + + m_szTyped[m_iTypedPos] = iNumber + '0'; + m_iTypedPos++; + m_szTyped[m_iTypedPos] = 0; + int iValue; + sscanf(m_szTyped, "%i", &iValue); + switch (m_iType) + { + case SPIN_CONTROL_TYPE_INT: + { + if (iValue < m_iStart || iValue > m_iEnd) + { + m_iTypedPos = 0; + m_szTyped[m_iTypedPos] = iNumber + '0'; + m_iTypedPos++; + m_szTyped[m_iTypedPos] = 0; + sscanf(m_szTyped, "%i", &iValue); + if (iValue < m_iStart || iValue > m_iEnd) + { + m_iTypedPos = 0; + strcpy(m_szTyped, ""); + return true; + } + } + m_iValue = iValue; + CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); + SendWindowMessage(msg); + } + break; + + case SPIN_CONTROL_TYPE_TEXT: + { + if (iValue < 0 || iValue >= (int)m_vecLabels.size()) + { + m_iTypedPos = 0; + m_szTyped[m_iTypedPos] = iNumber + '0'; + m_iTypedPos++; + m_szTyped[m_iTypedPos] = 0; + sscanf(m_szTyped, "%i", &iValue); + if (iValue < 0 || iValue >= (int)m_vecLabels.size()) + { + m_iTypedPos = 0; + strcpy(m_szTyped, ""); + return true; + } + } + m_iValue = iValue; + CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); + SendWindowMessage(msg); + } + break; + + } + return true; + } + break; + case ACTION_PAGE_UP: + if (!m_bReverse) + PageDown(); + else + PageUp(); + return true; + break; + case ACTION_PAGE_DOWN: + if (!m_bReverse) + PageUp(); + else + PageDown(); + return true; + break; + case ACTION_SELECT_ITEM: + if (m_iSelect == SPIN_BUTTON_UP) + { + MoveUp(); + return true; + } + if (m_iSelect == SPIN_BUTTON_DOWN) + { + MoveDown(); + return true; + } + break; + } +/* static float m_fSmoothScrollOffset = 0.0f; + if (action.id == ACTION_SCROLL_UP) + { + m_fSmoothScrollOffset += action.amount1 * action.amount1; + bool handled = false; + while (m_fSmoothScrollOffset > 0.4) + { + handled = true; + m_fSmoothScrollOffset -= 0.4f; + MoveDown(); + } + return handled; + }*/ + return CGUIControl::OnAction(action); +} + +void CGUISpinControl::OnLeft() +{ + if (m_iSelect == SPIN_BUTTON_UP) + { + // select the down button + m_iSelect = SPIN_BUTTON_DOWN; + } + else + { // base class + CGUIControl::OnLeft(); + } +} + +void CGUISpinControl::OnRight() +{ + if (m_iSelect == SPIN_BUTTON_DOWN) + { + // select the up button + m_iSelect = SPIN_BUTTON_UP; + } + else + { // base class + CGUIControl::OnRight(); + } +} + +void CGUISpinControl::Clear() +{ + m_vecLabels.erase(m_vecLabels.begin(), m_vecLabels.end()); + m_vecValues.erase(m_vecValues.begin(), m_vecValues.end()); + SetValue(0); +} + +bool CGUISpinControl::OnMessage(CGUIMessage& message) +{ + if (CGUIControl::OnMessage(message) ) + return true; + if (message.GetControlId() == GetID() ) + { + switch (message.GetMessage()) + { + case GUI_MSG_ITEM_SELECT: + if (SPIN_CONTROL_TYPE_PAGE == m_iType) + { + m_currentItem = message.GetParam1(); + return true; + } + SetValue( message.GetParam1()); + if (message.GetParam2() == SPIN_BUTTON_DOWN || message.GetParam2() == SPIN_BUTTON_UP) + m_iSelect = message.GetParam2(); + return true; + break; + + case GUI_MSG_LABEL_RESET: + if (SPIN_CONTROL_TYPE_PAGE == m_iType) + { + m_itemsPerPage = message.GetParam1(); + m_numItems = message.GetParam2(); + return true; + } + { + Clear(); + return true; + } + break; + + case GUI_MSG_SHOWRANGE: + if (message.GetParam1() ) + m_bShowRange = true; + else + m_bShowRange = false; + break; + + case GUI_MSG_LABEL_ADD: + { + AddLabel(message.GetLabel(), message.GetParam1()); + return true; + } + break; + + case GUI_MSG_ITEM_SELECTED: + { + message.SetParam1( GetValue() ); + message.SetParam2(m_iSelect); + + if (m_iType == SPIN_CONTROL_TYPE_TEXT) + { + if ( m_iValue >= 0 && m_iValue < (int)m_vecLabels.size() ) + message.SetLabel( m_vecLabels[m_iValue]); + } + return true; + } + + case GUI_MSG_PAGE_UP: + if (CanMoveUp()) + MoveUp(); + return true; + + case GUI_MSG_PAGE_DOWN: + if (CanMoveDown()) + MoveDown(); + return true; + + case GUI_MSG_MOVE_OFFSET: + { + int count = (int)message.GetParam1(); + while (count < 0) + { + MoveUp(); + count++; + } + while (count > 0) + { + MoveDown(); + count--; + } + return true; + } + + } + } + return false; +} + +void CGUISpinControl::AllocResources() +{ + CGUIControl::AllocResources(); + m_imgspinUp.AllocResources(); + m_imgspinUpFocus.AllocResources(); + m_imgspinDown.AllocResources(); + m_imgspinDownFocus.AllocResources(); + + m_imgspinDownFocus.SetPosition(m_posX, m_posY); + m_imgspinDown.SetPosition(m_posX, m_posY); + m_imgspinUp.SetPosition(m_posX + m_imgspinDown.GetWidth(), m_posY); + m_imgspinUpFocus.SetPosition(m_posX + m_imgspinDownFocus.GetWidth(), m_posY); +} + +void CGUISpinControl::FreeResources() +{ + CGUIControl::FreeResources(); + m_imgspinUp.FreeResources(); + m_imgspinUpFocus.FreeResources(); + m_imgspinDown.FreeResources(); + m_imgspinDownFocus.FreeResources(); + m_iTypedPos = 0; + strcpy(m_szTyped, ""); +} + +void CGUISpinControl::DynamicResourceAlloc(bool bOnOff) +{ + CGUIControl::DynamicResourceAlloc(bOnOff); + m_imgspinUp.DynamicResourceAlloc(bOnOff); + m_imgspinUpFocus.DynamicResourceAlloc(bOnOff); + m_imgspinDown.DynamicResourceAlloc(bOnOff); + m_imgspinDownFocus.DynamicResourceAlloc(bOnOff); +} + +void CGUISpinControl::Render() +{ + if (!HasFocus()) + { + m_iTypedPos = 0; + strcpy(m_szTyped, ""); + } + + float posX = m_posX; + CStdString text; + CStdStringW strTextUnicode; + + if (m_iType == SPIN_CONTROL_TYPE_INT) + { + if (m_bShowRange) + { + text.Format("%i/%i", m_iValue, m_iEnd); + } + else + { + text.Format("%i", m_iValue); + } + } + else if (m_iType == SPIN_CONTROL_TYPE_PAGE) + { + // work out number of pages and current page + int numPages = (m_numItems + m_itemsPerPage - 1) / m_itemsPerPage; + int currentPage = m_currentItem / m_itemsPerPage + 1; + if (m_currentItem >= m_numItems - m_itemsPerPage) + currentPage = numPages; + text.Format("%i/%i", currentPage, numPages); + } + else if (m_iType == SPIN_CONTROL_TYPE_FLOAT) + { + if (m_bShowRange) + { + text.Format("%02.2f/%02.2f", m_fValue, m_fEnd); + } + else + { + text.Format("%02.2f", m_fValue); + } + } + else + { + if (m_iValue >= 0 && m_iValue < (int)m_vecLabels.size() ) + { + if (m_bShowRange) + { + text.Format("(%i/%i) %s", m_iValue + 1, (int)m_vecLabels.size(), CStdString(m_vecLabels[m_iValue]).c_str() ); + } + else + { + text.Format("%s", CStdString(m_vecLabels[m_iValue]).c_str() ); + } + } + else text.Format("?%i?", m_iValue); + + } + + m_textLayout.Update(text); + // Calculate the size of our text (for use in HitTest) + float fTextWidth = 0; + float fTextHeight = 0; + m_textLayout.GetTextExtent(fTextWidth, fTextHeight); + // Position the arrows + if ( !(m_label.align & (XBFONT_RIGHT | XBFONT_CENTER_X)) ) + { + m_imgspinUpFocus.SetPosition(fTextWidth + 5 + posX + m_imgspinDown.GetWidth(), m_posY); + m_imgspinUp.SetPosition(fTextWidth + 5 + posX + m_imgspinDown.GetWidth(), m_posY); + m_imgspinDownFocus.SetPosition(fTextWidth + 5 + posX, m_posY); + m_imgspinDown.SetPosition(fTextWidth + 5 + posX, m_posY); + } + + if ( HasFocus() ) + { + if (m_iSelect == SPIN_BUTTON_UP) + m_imgspinUpFocus.Render(); + else + m_imgspinUp.Render(); + + if (m_iSelect == SPIN_BUTTON_DOWN) + m_imgspinDownFocus.Render(); + else + m_imgspinDown.Render(); + } + else + { + m_imgspinUp.Render(); + m_imgspinDown.Render(); + } + + if (m_label.font) + { + float fPosY; + if (m_label.align & XBFONT_CENTER_Y) + fPosY = m_posY + m_height * 0.5f; + else + fPosY = m_posY + m_label.offsetY; + + float fPosX = m_posX + m_label.offsetX - 3; + if (IsDisabled()) + m_textLayout.Render(fPosX, fPosY, 0, m_label.disabledColor, m_label.shadowColor, m_label.align, 0, true); + else if (HasFocus() && m_label.focusedColor) + m_textLayout.Render(fPosX, fPosY, 0, m_label.focusedColor, m_label.shadowColor, m_label.align, 0); + else + m_textLayout.Render(fPosX, fPosY, 0, m_label.textColor, m_label.shadowColor, m_label.align, 0); + + // set our hit rectangle for MouseOver events + if (!(m_label.align & (XBFONT_RIGHT | XBFONT_CENTER_X))) + m_hitRect.SetRect(fPosX, fPosY, fPosX + fTextWidth, fPosY + fTextHeight); + else + m_hitRect.SetRect(fPosX - fTextWidth, fPosY, fPosX, fPosY + fTextHeight); + } + CGUIControl::Render(); +} + +void CGUISpinControl::SetRange(int iStart, int iEnd) +{ + m_iStart = iStart; + m_iEnd = iEnd; +} + + +void CGUISpinControl::SetFloatRange(float fStart, float fEnd) +{ + m_fStart = fStart; + m_fEnd = fEnd; +} + +void CGUISpinControl::SetValueFromLabel(const CStdString &label) +{ + if (m_iType == SPIN_CONTROL_TYPE_TEXT) + { + m_iValue = 0; + for (unsigned int i = 0; i < m_vecLabels.size(); i++) + if (label == m_vecLabels[i]) + m_iValue = i; + } + else + m_iValue = atoi(label.c_str()); +} + +void CGUISpinControl::SetValue(int iValue) +{ + if (m_iType == SPIN_CONTROL_TYPE_TEXT) + { + m_iValue = 0; + for (unsigned int i = 0; i < m_vecValues.size(); i++) + if (iValue == m_vecValues[i]) + m_iValue = i; + } + else + m_iValue = iValue; +} + +void CGUISpinControl::SetFloatValue(float fValue) +{ + m_fValue = fValue; +} + +int CGUISpinControl::GetValue() const +{ + if (m_iType == SPIN_CONTROL_TYPE_TEXT) + { + if (m_iValue >= 0 && m_iValue < (int)m_vecValues.size()) + return m_vecValues[m_iValue]; + } + return m_iValue; +} + +float CGUISpinControl::GetFloatValue() const +{ + return m_fValue; +} + + +void CGUISpinControl::AddLabel(const string& strLabel, int iValue) +{ + m_vecLabels.push_back(strLabel); + m_vecValues.push_back(iValue); +} + +const string CGUISpinControl::GetLabel() const +{ + if (m_iValue >= 0 && m_iValue < (int)m_vecLabels.size()) + { + return m_vecLabels[ m_iValue]; + } + return ""; +} + +void CGUISpinControl::SetPosition(float posX, float posY) +{ + CGUIControl::SetPosition(posX, posY); + + m_imgspinDownFocus.SetPosition(posX, posY); + m_imgspinDown.SetPosition(posX, posY); + + m_imgspinUp.SetPosition(m_posX + m_imgspinDown.GetWidth(), m_posY); + m_imgspinUpFocus.SetPosition(m_posX + m_imgspinDownFocus.GetWidth(), m_posY); + +} + +float CGUISpinControl::GetWidth() const +{ + return m_imgspinDown.GetWidth() * 2 ; +} + +bool CGUISpinControl::CanMoveUp(bool bTestReverse) +{ + // test for reverse... + if (bTestReverse && m_bReverse) return CanMoveDown(false); + + switch (m_iType) + { + case SPIN_CONTROL_TYPE_PAGE: + return m_currentItem > 0; + case SPIN_CONTROL_TYPE_INT: + { + if (m_iValue - 1 >= m_iStart) + return true; + return false; + } + break; + + case SPIN_CONTROL_TYPE_FLOAT: + { + if (m_fValue - m_fInterval >= m_fStart) + return true; + return false; + } + break; + + case SPIN_CONTROL_TYPE_TEXT: + { + if (m_iValue - 1 >= 0) + return true; + return false; + } + break; + } + return false; +} + +bool CGUISpinControl::CanMoveDown(bool bTestReverse) +{ + // test for reverse... + if (bTestReverse && m_bReverse) return CanMoveUp(false); + switch (m_iType) + { + case SPIN_CONTROL_TYPE_PAGE: + return m_currentItem < m_numItems; + case SPIN_CONTROL_TYPE_INT: + { + if (m_iValue + 1 <= m_iEnd) + return true; + return false; + } + break; + + case SPIN_CONTROL_TYPE_FLOAT: + { + if (m_fValue + m_fInterval <= m_fEnd) + return true; + return false; + } + break; + + case SPIN_CONTROL_TYPE_TEXT: + { + if (m_iValue + 1 < (int)m_vecLabels.size()) + return true; + return false; + } + break; + } + return false; +} + +void CGUISpinControl::PageUp() +{ + switch (m_iType) + { + case SPIN_CONTROL_TYPE_INT: + { + if (m_iValue - 10 >= m_iStart) + m_iValue -= 10; + else + m_iValue = m_iStart; + CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); + SendWindowMessage(msg); + return ; + } + break; + case SPIN_CONTROL_TYPE_PAGE: + ChangePage(-10); + break; + case SPIN_CONTROL_TYPE_TEXT: + { + if (m_iValue - 10 >= 0) + m_iValue -= 10; + else + m_iValue = 0; + CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); + SendWindowMessage(msg); + return ; + } + break; + } + +} + +void CGUISpinControl::PageDown() +{ + switch (m_iType) + { + case SPIN_CONTROL_TYPE_INT: + { + if (m_iValue + 10 <= m_iEnd) + m_iValue += 10; + else + m_iValue = m_iEnd; + CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); + SendWindowMessage(msg); + return ; + } + break; + case SPIN_CONTROL_TYPE_PAGE: + ChangePage(10); + break; + case SPIN_CONTROL_TYPE_TEXT: + { + if (m_iValue + 10 < (int)m_vecLabels.size() ) + m_iValue += 10; + CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); + SendWindowMessage(msg); + } + break; + } +} + +void CGUISpinControl::MoveUp(bool bTestReverse) +{ + if (bTestReverse && m_bReverse) + { // actually should move down. + MoveDown(false); + return ; + } + switch (m_iType) + { + case SPIN_CONTROL_TYPE_INT: + { + if (m_iValue - 1 >= m_iStart) + m_iValue--; + else if (m_iValue == m_iStart) + m_iValue = m_iEnd; + CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); + SendWindowMessage(msg); + return ; + } + break; + + case SPIN_CONTROL_TYPE_PAGE: + ChangePage(-1); + break; + + case SPIN_CONTROL_TYPE_FLOAT: + { + if (m_fValue - m_fInterval >= m_fStart) + m_fValue -= m_fInterval; + else if (m_fValue - m_fInterval < m_fStart) + m_fValue = m_fEnd; + CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); + SendWindowMessage(msg); + return ; + } + break; + + case SPIN_CONTROL_TYPE_TEXT: + { + if (m_iValue - 1 >= 0) + m_iValue--; + else if (m_iValue == 0) + m_iValue = (int)m_vecLabels.size() - 1; + CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); + SendWindowMessage(msg); + return ; + } + break; + } +} + +void CGUISpinControl::MoveDown(bool bTestReverse) +{ + if (bTestReverse && m_bReverse) + { // actually should move up. + MoveUp(false); + return ; + } + switch (m_iType) + { + case SPIN_CONTROL_TYPE_INT: + { + if (m_iValue + 1 <= m_iEnd) + m_iValue++; + else if (m_iValue == m_iEnd) + m_iValue = m_iStart; + CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); + SendWindowMessage(msg); + return ; + } + break; + + case SPIN_CONTROL_TYPE_PAGE: + ChangePage(1); + break; + + case SPIN_CONTROL_TYPE_FLOAT: + { + if (m_fValue + m_fInterval <= m_fEnd) + m_fValue += m_fInterval; + else if (m_fValue + m_fInterval > m_fEnd) + m_fValue = m_fStart; + CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); + SendWindowMessage(msg); + return ; + } + break; + + case SPIN_CONTROL_TYPE_TEXT: + { + if (m_iValue + 1 < (int)m_vecLabels.size() ) + m_iValue++; + else if (m_iValue == (int)m_vecLabels.size() - 1) + m_iValue = 0; + CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); + SendWindowMessage(msg); + return ; + } + break; + } +} +void CGUISpinControl::SetReverse(bool bReverse) +{ + m_bReverse = bReverse; +} + +void CGUISpinControl::SetFloatInterval(float fInterval) +{ + m_fInterval = fInterval; +} + +void CGUISpinControl::SetShowRange(bool bOnoff) +{ + m_bShowRange = bOnoff; +} + +int CGUISpinControl::GetMinimum() const +{ + switch (m_iType) + { + case SPIN_CONTROL_TYPE_PAGE: + return 0; + case SPIN_CONTROL_TYPE_INT: + return m_iStart; + break; + + case SPIN_CONTROL_TYPE_TEXT: + return 1; + break; + + case SPIN_CONTROL_TYPE_FLOAT: + return (int)(m_fStart*10.0f); + break; + } + return 0; +} + +int CGUISpinControl::GetMaximum() const +{ + switch (m_iType) + { + case SPIN_CONTROL_TYPE_PAGE: + return m_numItems; + case SPIN_CONTROL_TYPE_INT: + return m_iEnd; + break; + + case SPIN_CONTROL_TYPE_TEXT: + return (int)m_vecLabels.size(); + break; + + case SPIN_CONTROL_TYPE_FLOAT: + return (int)(m_fEnd*10.0f); + break; + } + return 100; +} + +bool CGUISpinControl::HitTest(const CPoint &point) const +{ + if (m_imgspinUpFocus.HitTest(point) || m_imgspinDownFocus.HitTest(point)) + return true; + return CGUIControl::HitTest(point); +} + +bool CGUISpinControl::OnMouseOver(const CPoint &point) +{ + if (m_imgspinUpFocus.HitTest(point)) + { + CGUIControl::OnMouseOver(point); + m_iSelect = SPIN_BUTTON_UP; + } + else if (m_imgspinDownFocus.HitTest(point)) + { + CGUIControl::OnMouseOver(point); + m_iSelect = SPIN_BUTTON_DOWN; + } + else + { + CGUIControl::OnMouseOver(point); + m_iSelect = SPIN_BUTTON_UP; + } + return true; +} + +bool CGUISpinControl::OnMouseClick(int button, const CPoint &point) +{ // only left button handled + if (button != MOUSE_LEFT_BUTTON) return false; + if (m_imgspinUpFocus.HitTest(point)) + { + MoveUp(); + } + if (m_imgspinDownFocus.HitTest(point)) + { + MoveDown(); + } + return true; +} + +bool CGUISpinControl::OnMouseWheel(char wheel, const CPoint &point) +{ + for (int i = 0; i < abs(wheel); i++) + { + if (wheel > 0) + { + MoveUp(); + } + else + { + MoveDown(); + } + } + return true; +} + +CStdString CGUISpinControl::GetDescription() const +{ + CStdString strLabel; + strLabel.Format("%i/%i", 1 + GetValue(), GetMaximum()); + return strLabel; +} + +bool CGUISpinControl::IsFocusedOnUp() const +{ + return (m_iSelect == SPIN_BUTTON_UP); +} + +void CGUISpinControl::ChangePage(int amount) +{ + m_currentItem += amount * m_itemsPerPage; + if (m_currentItem > m_numItems - m_itemsPerPage) + m_currentItem = m_numItems - m_itemsPerPage; + if (m_currentItem < 0) + m_currentItem = 0; + CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetParentID(), GetID(), GUI_MSG_PAGE_CHANGE, m_currentItem); + SendWindowMessage(message); +} + +void CGUISpinControl::UpdateColors() +{ + m_label.UpdateColors(); + CGUIControl::UpdateColors(); + m_imgspinDownFocus.SetDiffuseColor(m_diffuseColor); + m_imgspinDown.SetDiffuseColor(m_diffuseColor); + m_imgspinUp.SetDiffuseColor(m_diffuseColor); + m_imgspinUpFocus.SetDiffuseColor(m_diffuseColor); +} + +bool CGUISpinControl::IsVisible() const +{ + // page controls can be optionally disabled if the number of pages is 1 + if (m_iType == SPIN_CONTROL_TYPE_PAGE && m_numItems <= m_itemsPerPage && !m_showOnePage) + return false; + return CGUIControl::IsVisible(); +} diff --git a/guilib/GUISpinControl.h b/guilib/GUISpinControl.h new file mode 100644 index 0000000000..67ea5956c4 --- /dev/null +++ b/guilib/GUISpinControl.h @@ -0,0 +1,127 @@ +/*! +\file GUISpinControl.h +\brief +*/ + +#ifndef GUILIB_SPINCONTROL_H +#define GUILIB_SPINCONTROL_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControl.h" +#include "GUITexture.h" +#include "GUITextLayout.h" + +#define SPIN_CONTROL_TYPE_INT 1 +#define SPIN_CONTROL_TYPE_FLOAT 2 +#define SPIN_CONTROL_TYPE_TEXT 3 +#define SPIN_CONTROL_TYPE_PAGE 4 + +/*! + \ingroup controls + \brief + */ +class CGUISpinControl : public CGUIControl +{ +public: + CGUISpinControl(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& textureUp, const CTextureInfo& textureDown, const CTextureInfo& textureUpFocus, const CTextureInfo& textureDownFocus, const CLabelInfo& labelInfo, int iType); + virtual ~CGUISpinControl(void); + virtual CGUISpinControl *Clone() const { return new CGUISpinControl(*this); }; + + virtual void Render(); + virtual bool OnAction(const CAction &action); + virtual void OnLeft(); + virtual void OnRight(); + virtual bool HitTest(const CPoint &point) const; + virtual bool OnMouseOver(const CPoint &point); + virtual bool OnMouseClick(int button, const CPoint &point); + virtual bool OnMouseWheel(char wheel, const CPoint &point); + virtual bool OnMessage(CGUIMessage& message); + virtual void AllocResources(); + virtual void FreeResources(); + virtual void DynamicResourceAlloc(bool bOnOff); + virtual void SetPosition(float posX, float posY); + virtual float GetWidth() const; + void SetRange(int iStart, int iEnd); + void SetFloatRange(float fStart, float fEnd); + void SetValue(int iValue); + void SetValueFromLabel(const CStdString &label); + void SetFloatValue(float fValue); + int GetValue() const; + float GetFloatValue() const; + void AddLabel(const std::string& strLabel, int iValue); + const std::string GetLabel() const; + void SetReverse(bool bOnOff); + int GetMaximum() const; + int GetMinimum() const; + void SetSpinAlign(uint32_t align, float offsetX) { m_label.align = align; m_label.offsetX = offsetX; }; + void SetType(int iType) { m_iType = iType; }; + float GetSpinWidth() const { return m_imgspinUp.GetWidth(); }; + float GetSpinHeight() const { return m_imgspinUp.GetHeight(); }; + void SetFloatInterval(float fInterval); + void SetShowRange(bool bOnoff) ; + void SetShowOnePage(bool showOnePage) { m_showOnePage = showOnePage; }; + void Clear(); + virtual CStdString GetDescription() const; + bool IsFocusedOnUp() const; + + virtual bool IsVisible() const; + +protected: + virtual void UpdateColors(); + void PageUp(); + void PageDown(); + bool CanMoveDown(bool bTestReverse = true); + bool CanMoveUp(bool bTestReverse = true); + void MoveUp(bool bTestReverse = true); + void MoveDown(bool bTestReverse = true); + void ChangePage(int amount); + int m_iStart; + int m_iEnd; + float m_fStart; + float m_fEnd; + int m_iValue; + float m_fValue; + int m_iType; + int m_iSelect; + bool m_bReverse; + float m_fInterval; + std::vector<std::string> m_vecLabels; + std::vector<int> m_vecValues; + CGUITexture m_imgspinUp; + CGUITexture m_imgspinDown; + CGUITexture m_imgspinUpFocus; + CGUITexture m_imgspinDownFocus; + CGUITextLayout m_textLayout; + CLabelInfo m_label; + bool m_bShowRange; + char m_szTyped[10]; + int m_iTypedPos; + + int m_currentItem; + int m_itemsPerPage; + int m_numItems; + bool m_showOnePage; +}; +#endif diff --git a/guilib/GUISpinControlEx.cpp b/guilib/GUISpinControlEx.cpp new file mode 100644 index 0000000000..8c9517cf0e --- /dev/null +++ b/guilib/GUISpinControlEx.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUISpinControlEx.h" + +CGUISpinControlEx::CGUISpinControlEx(int parentID, int controlID, float posX, float posY, float width, float height, float spinWidth, float spinHeight, const CLabelInfo& spinInfo, const CTextureInfo &textureFocus, const CTextureInfo &textureNoFocus, const CTextureInfo& textureUp, const CTextureInfo& textureDown, const CTextureInfo& textureUpFocus, const CTextureInfo& textureDownFocus, const CLabelInfo& labelInfo, int iType) + : CGUISpinControl(parentID, controlID, posX, posY, spinWidth, spinHeight, textureUp, textureDown, textureUpFocus, textureDownFocus, spinInfo, iType) + , m_buttonControl(parentID, controlID, posX, posY, width, height, textureFocus, textureNoFocus, labelInfo) +{ + ControlType = GUICONTROL_SPINEX; + m_spinPosX = 0; +} + +CGUISpinControlEx::~CGUISpinControlEx(void) +{ +} + +void CGUISpinControlEx::AllocResources() +{ + // Correct alignment - we always align the spincontrol on the right, + // and we always use a negative offsetX + m_label.align = (m_label.align & 4) | XBFONT_RIGHT; + if (m_label.offsetX > 0) + m_label.offsetX = -m_label.offsetX; + CGUISpinControl::AllocResources(); + m_buttonControl.AllocResources(); + if (m_height == 0) + m_height = GetSpinHeight(); +} + +void CGUISpinControlEx::FreeResources() +{ + CGUISpinControl::FreeResources(); + m_buttonControl.FreeResources(); +} + +void CGUISpinControlEx::DynamicResourceAlloc(bool bOnOff) +{ + CGUISpinControl::DynamicResourceAlloc(bOnOff); + m_buttonControl.DynamicResourceAlloc(bOnOff); +} + +void CGUISpinControlEx::Render() +{ + // make sure the button has focus if it should have... + m_buttonControl.SetFocus(HasFocus()); + m_buttonControl.SetPulseOnSelect(m_pulseOnSelect); + m_buttonControl.Render(); + if (m_bInvalidated) + SetPosition(GetXPosition(), GetYPosition()); + + CGUISpinControl::Render(); +} + +void CGUISpinControlEx::SetPosition(float posX, float posY) +{ + m_buttonControl.SetPosition(posX, posY); + float spinPosX = posX + m_buttonControl.GetWidth() - GetSpinWidth() * 2 - (m_spinPosX ? m_spinPosX : m_buttonControl.GetLabelInfo().offsetX); + float spinPosY = posY + (m_buttonControl.GetHeight() - GetSpinHeight()) * 0.5f; + CGUISpinControl::SetPosition(spinPosX, spinPosY); +} + +void CGUISpinControlEx::SetWidth(float width) +{ + m_buttonControl.SetWidth(width); + SetPosition(m_buttonControl.GetXPosition(), m_buttonControl.GetYPosition()); +} + +void CGUISpinControlEx::SetHeight(float height) +{ + m_buttonControl.SetHeight(height); + SetPosition(m_buttonControl.GetXPosition(), m_buttonControl.GetYPosition()); +} + +void CGUISpinControlEx::SetVisible(bool bVisible) +{ + m_buttonControl.SetVisible(bVisible); + CGUISpinControl::SetVisible(bVisible); +} + +void CGUISpinControlEx::UpdateColors() +{ + CGUISpinControl::UpdateColors(); + m_buttonControl.SetColorDiffuse(m_diffuseColor); + m_buttonControl.UpdateColors(); +} + +void CGUISpinControlEx::SetEnabled(bool bEnable) +{ + m_buttonControl.SetEnabled(bEnable); + CGUISpinControl::SetEnabled(bEnable); +} + +const CStdString CGUISpinControlEx::GetCurrentLabel() const +{ + return CGUISpinControl::GetLabel(); +} + +CStdString CGUISpinControlEx::GetDescription() const +{ + CStdString strLabel; + strLabel.Format("%s (%s)", m_buttonControl.GetDescription(), GetLabel()); + return strLabel; +} + +void CGUISpinControlEx::SettingsCategorySetSpinTextColor(const CGUIInfoColor &color) +{ + m_label.textColor = color; + m_label.focusedColor = color; +} + +void CGUISpinControlEx::SetSpinPosition(float spinPosX) +{ + m_spinPosX = spinPosX; + SetPosition(m_buttonControl.GetXPosition(), m_buttonControl.GetYPosition()); +} diff --git a/guilib/GUISpinControlEx.h b/guilib/GUISpinControlEx.h new file mode 100644 index 0000000000..f82f07e599 --- /dev/null +++ b/guilib/GUISpinControlEx.h @@ -0,0 +1,72 @@ +/*! +\file GUISpinControlEx.h +\brief +*/ + +#ifndef GUILIB_SPINCONTROLEX_H +#define GUILIB_SPINCONTROLEX_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUISpinControl.h" +#include "GUIButtonControl.h" + +/*! + \ingroup controls + \brief + */ +class CGUISpinControlEx : public CGUISpinControl +{ +public: + CGUISpinControlEx(int parentID, int controlID, float posX, float posY, float width, float height, float spinWidth, float spinHeight, const CLabelInfo& spinInfo, const CTextureInfo &textureFocus, const CTextureInfo &textureNoFocus, const CTextureInfo& textureUp, const CTextureInfo& textureDown, const CTextureInfo& textureUpFocus, const CTextureInfo& textureDownFocus, const CLabelInfo& labelInfo, int iType); + virtual ~CGUISpinControlEx(void); + virtual CGUISpinControlEx *Clone() const { return new CGUISpinControlEx(*this); }; + + virtual void Render(); + virtual void SetPosition(float posX, float posY); + virtual float GetWidth() const { return m_buttonControl.GetWidth();}; + virtual void SetWidth(float width); + virtual float GetHeight() const { return m_buttonControl.GetHeight();}; + virtual void SetHeight(float height); + virtual void AllocResources(); + virtual void FreeResources(); + virtual void DynamicResourceAlloc(bool bOnOff); + const CStdString GetCurrentLabel() const; + void SetText(const std::string & aLabel) {m_buttonControl.SetLabel(aLabel);}; + virtual void SetVisible(bool bVisible); + const CLabelInfo& GetButtonLabelInfo() { return m_buttonControl.GetLabelInfo(); }; + virtual void SetEnabled(bool bEnable); + virtual float GetXPosition() const { return m_buttonControl.GetXPosition();}; + virtual float GetYPosition() const { return m_buttonControl.GetYPosition();}; + virtual CStdString GetDescription() const; + virtual bool HitTest(const CPoint &point) const { return m_buttonControl.HitTest(point); }; + void SetSpinPosition(float spinPosX); + + void SettingsCategorySetSpinTextColor(const CGUIInfoColor &color); +protected: + virtual void UpdateColors(); + CGUIButtonControl m_buttonControl; + float m_spinPosX; +}; +#endif diff --git a/guilib/GUIStandardWindow.cpp b/guilib/GUIStandardWindow.cpp new file mode 100644 index 0000000000..971a9cee9a --- /dev/null +++ b/guilib/GUIStandardWindow.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIStandardWindow.h" +#include "GUIWindowManager.h" +#include "AdvancedSettings.h" +#include "Key.h" + +CGUIStandardWindow::CGUIStandardWindow(void) : CGUIWindow(0, "") +{ +} + +CGUIStandardWindow::~CGUIStandardWindow(void) +{ +} + +bool CGUIStandardWindow::OnAction(const CAction &action) +{ + if (action.id == ACTION_PREVIOUS_MENU) + { + m_gWindowManager.PreviousWindow(); + return true; + } + + if (action.id == ACTION_PARENT_DIR && g_advancedSettings.m_bUseEvilB) + { + m_gWindowManager.PreviousWindow(); + return true; + } + + return CGUIWindow::OnAction(action); +} diff --git a/guilib/GUIStandardWindow.h b/guilib/GUIStandardWindow.h new file mode 100644 index 0000000000..94e57b5fc5 --- /dev/null +++ b/guilib/GUIStandardWindow.h @@ -0,0 +1,39 @@ +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIWindow.h" + +// This class is designed to be the base class for any standard +// full screen window. Default implementations for action keys +// can be placed into this class to make creating new window +// classes that much easier. + +class CGUIStandardWindow : + public CGUIWindow +{ +public: + CGUIStandardWindow(void); + virtual ~CGUIStandardWindow(void); + + virtual bool OnAction(const CAction &action); +}; diff --git a/guilib/GUITextBox.cpp b/guilib/GUITextBox.cpp new file mode 100644 index 0000000000..b424af6f15 --- /dev/null +++ b/guilib/GUITextBox.cpp @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUITextBox.h" +#include "utils/CharsetConverter.h" +#include "utils/GUIInfoManager.h" +#include "tinyXML/tinyxml.h" + +using namespace std; + +CGUITextBox::CGUITextBox(int parentID, int controlID, float posX, float posY, float width, float height, + const CLabelInfo& labelInfo, int scrollTime) + : CGUIControl(parentID, controlID, posX, posY, width, height) + , CGUITextLayout(labelInfo.font, true) +{ + m_offset = 0; + m_scrollOffset = 0; + m_scrollSpeed = 0; + m_itemsPerPage = 10; + m_itemHeight = 10; + ControlType = GUICONTROL_TEXTBOX; + m_pageControl = 0; + m_renderTime = 0; + m_lastRenderTime = 0; + m_scrollTime = scrollTime; + m_autoScrollCondition = 0; + m_autoScrollTime = 0; + m_autoScrollDelay = 3000; + m_autoScrollDelayTime = 0; + m_autoScrollRepeatAnim = NULL; + m_label = labelInfo; +} + +CGUITextBox::CGUITextBox(const CGUITextBox &from) +: CGUIControl(from), CGUITextLayout(from) +{ + m_pageControl = from.m_pageControl; + m_scrollTime = from.m_scrollTime; + m_autoScrollCondition = from.m_autoScrollCondition; + m_autoScrollTime = from.m_autoScrollTime; + m_autoScrollDelay = from.m_autoScrollDelay; + m_autoScrollRepeatAnim = NULL; + m_label = from.m_label; + m_info = from.m_info; + // defaults + m_offset = 0; + m_scrollOffset = 0; + m_scrollSpeed = 0; + m_itemsPerPage = 10; + m_itemHeight = 10; + m_renderTime = 0; + m_lastRenderTime = 0; + m_autoScrollDelayTime = 0; + ControlType = GUICONTROL_TEXTBOX; +} + +CGUITextBox::~CGUITextBox(void) +{ + delete m_autoScrollRepeatAnim; + m_autoScrollRepeatAnim = NULL; +} + +void CGUITextBox::DoRender(DWORD currentTime) +{ + m_renderTime = currentTime; + + // render the repeat anim as appropriate + if (m_autoScrollRepeatAnim) + { + m_autoScrollRepeatAnim->Animate(m_renderTime, true); + TransformMatrix matrix; + m_autoScrollRepeatAnim->RenderAnimation(matrix); + g_graphicsContext.AddTransform(matrix); + } + + CGUIControl::DoRender(currentTime); + // if not visible, we reset the autoscroll timer and positioning + if (!IsVisible() && m_autoScrollTime) + { + ResetAutoScrolling(); + m_lastRenderTime = 0; + m_offset = 0; + m_scrollOffset = 0; + m_scrollSpeed = 0; + } + if (m_autoScrollRepeatAnim) + g_graphicsContext.RemoveTransform(); +} + +void CGUITextBox::UpdateColors() +{ + m_label.UpdateColors(); + CGUIControl::UpdateColors(); +} + +void CGUITextBox::UpdateInfo(const CGUIListItem *item) +{ + m_textColor = m_label.textColor; + if (!CGUITextLayout::Update(item ? m_info.GetItemLabel(item) : m_info.GetLabel(m_parentID), m_width)) + return; // nothing changed + + // needed update, so reset to the top of the textbox and update our sizing/page control + m_offset = 0; + m_scrollOffset = 0; + ResetAutoScrolling(); + + m_itemHeight = m_font->GetLineHeight(); + m_itemsPerPage = (unsigned int)(m_height / m_itemHeight); + + UpdatePageControl(); +} + +void CGUITextBox::Render() +{ + // update our auto-scrolling as necessary + if (m_autoScrollTime && m_lines.size() > m_itemsPerPage) + { + if (!m_autoScrollCondition || g_infoManager.GetBool(m_autoScrollCondition, m_parentID)) + { + if (m_lastRenderTime) + m_autoScrollDelayTime += m_renderTime - m_lastRenderTime; + if (m_autoScrollDelayTime > (unsigned int)m_autoScrollDelay && m_scrollSpeed == 0) + { // delay is finished - start scrolling + if (m_offset < (int)m_lines.size() - m_itemsPerPage) + ScrollToOffset(m_offset + 1, true); + else + { // at the end, run a delay and restart + if (m_autoScrollRepeatAnim) + { + if (m_autoScrollRepeatAnim->GetState() == ANIM_STATE_NONE) + m_autoScrollRepeatAnim->QueueAnimation(ANIM_PROCESS_NORMAL); + else if (m_autoScrollRepeatAnim->GetState() == ANIM_STATE_APPLIED) + { // reset to the start of the list and start the scrolling again + m_offset = 0; + m_scrollOffset = 0; + ResetAutoScrolling(); + } + } + } + } + } + else if (m_autoScrollCondition) + ResetAutoScrolling(); // conditional is false, so reset the autoscrolling + } + + // update our scroll position as necessary + if (m_lastRenderTime) + m_scrollOffset += m_scrollSpeed * (m_renderTime - m_lastRenderTime); + if ((m_scrollSpeed < 0 && m_scrollOffset < m_offset * m_itemHeight) || + (m_scrollSpeed > 0 && m_scrollOffset > m_offset * m_itemHeight)) + { + m_scrollOffset = m_offset * m_itemHeight; + m_scrollSpeed = 0; + } + m_lastRenderTime = m_renderTime; + + int offset = (int)(m_scrollOffset / m_itemHeight); + + g_graphicsContext.SetClipRegion(m_posX, m_posY, m_width, m_height); + + // we offset our draw position to take into account scrolling and whether or not our focused + // item is offscreen "above" the list. + float posX = m_posX; + float posY = m_posY + offset * m_itemHeight - m_scrollOffset; + + // alignment correction + if (m_label.align & XBFONT_CENTER_X) + posX += m_width * 0.5f; + if (m_label.align & XBFONT_RIGHT) + posX += m_width; + + if (m_font) + { + m_font->Begin(); + int current = offset; + while (posY < m_posY + m_height && current < (int)m_lines.size()) + { + uint32_t align = m_label.align; + if (m_lines[current].m_text.size() && m_lines[current].m_carriageReturn) + align &= ~XBFONT_JUSTIFIED; // last line of a paragraph shouldn't be justified + m_font->DrawText(posX, posY + 2, m_colors, m_label.shadowColor, m_lines[current].m_text, align, m_width); + posY += m_itemHeight; + current++; + } + m_font->End(); + } + + g_graphicsContext.RestoreClipRegion(); + + if (m_pageControl) + { + CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(), m_pageControl, offset); + SendWindowMessage(msg); + } + CGUIControl::Render(); +} + +bool CGUITextBox::OnMessage(CGUIMessage& message) +{ + if (message.GetControlId() == GetID()) + { + if (message.GetMessage() == GUI_MSG_LABEL_SET) + { + m_offset = 0; + m_scrollOffset = 0; + ResetAutoScrolling(); + CGUITextLayout::Reset(); + m_info.SetLabel(message.GetLabel(), ""); + } + + if (message.GetMessage() == GUI_MSG_LABEL_RESET) + { + m_offset = 0; + m_scrollOffset = 0; + ResetAutoScrolling(); + CGUITextLayout::Reset(); + if (m_pageControl) + { + CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), m_pageControl, m_itemsPerPage, m_lines.size()); + SendWindowMessage(msg); + } + } + + if (message.GetMessage() == GUI_MSG_PAGE_CHANGE) + { + if (message.GetSenderId() == m_pageControl) + { // update our page + Scroll(message.GetParam1()); + return true; + } + } + } + + return CGUIControl::OnMessage(message); +} + +void CGUITextBox::UpdatePageControl() +{ + if (m_pageControl) + { + CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), m_pageControl, m_itemsPerPage, m_lines.size()); + SendWindowMessage(msg); + } +} + +bool CGUITextBox::CanFocus() const +{ + return false; +} + +void CGUITextBox::SetPageControl(int pageControl) +{ + m_pageControl = pageControl; +} + +void CGUITextBox::SetInfo(const CGUIInfoLabel &infoLabel) +{ + m_info = infoLabel; +} + +void CGUITextBox::Scroll(unsigned int offset) +{ + ResetAutoScrolling(); + if (m_lines.size() <= m_itemsPerPage) + return; // no need to scroll + if (offset > m_lines.size() - m_itemsPerPage) + offset = m_lines.size() - m_itemsPerPage; // on last page + ScrollToOffset(offset); +} + +void CGUITextBox::ScrollToOffset(int offset, bool autoScroll) +{ + m_scrollOffset = m_offset * m_itemHeight; + int timeToScroll = autoScroll ? m_autoScrollTime : m_scrollTime; + m_scrollSpeed = (offset * m_itemHeight - m_scrollOffset) / timeToScroll; + m_offset = offset; +} + +void CGUITextBox::SetAutoScrolling(const TiXmlNode *node) +{ + if (!node) return; + const TiXmlElement *scroll = node->FirstChildElement("autoscroll"); + if (scroll) + { + scroll->Attribute("delay", &m_autoScrollDelay); + scroll->Attribute("time", &m_autoScrollTime); + if (scroll->FirstChild()) + m_autoScrollCondition = g_infoManager.TranslateString(scroll->FirstChild()->ValueStr()); + int repeatTime; + if (scroll->Attribute("repeat", &repeatTime)) + m_autoScrollRepeatAnim = CAnimation::CreateFader(100, 0, repeatTime, 1000); + } +} + +void CGUITextBox::ResetAutoScrolling() +{ + m_autoScrollDelayTime = 0; + if (m_autoScrollRepeatAnim) + m_autoScrollRepeatAnim->ResetAnimation(); +} + +unsigned int CGUITextBox::GetRows() const +{ + return m_lines.size(); +} + +int CGUITextBox::GetCurrentPage() const +{ + if (m_offset + m_itemsPerPage >= GetRows()) // last page + return (GetRows() + m_itemsPerPage - 1) / m_itemsPerPage; + return m_offset / m_itemsPerPage + 1; +} + +CStdString CGUITextBox::GetLabel(int info) const +{ + CStdString label; + switch (info) + { + case CONTAINER_NUM_PAGES: + label.Format("%u", (GetRows() + m_itemsPerPage - 1) / m_itemsPerPage); + break; + case CONTAINER_CURRENT_PAGE: + label.Format("%u", GetCurrentPage()); + break; + default: + break; + } + return label; +} diff --git a/guilib/GUITextBox.h b/guilib/GUITextBox.h new file mode 100644 index 0000000000..c4ab36eef4 --- /dev/null +++ b/guilib/GUITextBox.h @@ -0,0 +1,96 @@ +/*! +\file GUITextBox.h +\brief +*/ + +#ifndef GUILIB_GUITEXTBOX_H +#define GUILIB_GUITEXTBOX_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUILabelControl.h" +#include "GUITextLayout.h" + +/*! + \ingroup controls + \brief + */ + +class TiXmlNode; + +class CGUITextBox : public CGUIControl, public CGUITextLayout +{ +public: + CGUITextBox(int parentID, int controlID, float posX, float posY, float width, float height, + const CLabelInfo &labelInfo, int scrollTime = 200); + CGUITextBox(const CGUITextBox &from); + virtual ~CGUITextBox(void); + virtual CGUITextBox *Clone() const { return new CGUITextBox(*this); }; + + virtual void DoRender(DWORD currentTime); + virtual void Render(); + virtual bool OnMessage(CGUIMessage& message); + + void SetPageControl(int pageControl); + + virtual bool CanFocus() const; + void SetInfo(const CGUIInfoLabel &info); + void SetAutoScrolling(const TiXmlNode *node); + void ResetAutoScrolling(); + CStdString GetLabel(int info) const; + + void Scroll(unsigned int offset); + +protected: + virtual void UpdateColors(); + virtual void UpdateInfo(const CGUIListItem *item = NULL); + void UpdatePageControl(); + void ScrollToOffset(int offset, bool autoScroll = false); + unsigned int GetRows() const; + int GetCurrentPage() const; + + // offset of text in the control for scrolling + unsigned int m_offset; + float m_scrollOffset; + float m_scrollSpeed; + int m_scrollTime; + unsigned int m_itemsPerPage; + float m_itemHeight; + DWORD m_renderTime; + DWORD m_lastRenderTime; + + CLabelInfo m_label; + + // autoscrolling + int m_autoScrollCondition; + int m_autoScrollTime; // time to scroll 1 line (ms) + int m_autoScrollDelay; // delay before scroll (ms) + DWORD m_autoScrollDelayTime; // current offset into the delay + CAnimation *m_autoScrollRepeatAnim; + + int m_pageControl; + + CGUIInfoLabel m_info; +}; +#endif diff --git a/guilib/GUITextLayout.cpp b/guilib/GUITextLayout.cpp new file mode 100644 index 0000000000..b283080dfb --- /dev/null +++ b/guilib/GUITextLayout.cpp @@ -0,0 +1,635 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUITextLayout.h" +#include "GUIFont.h" +#include "GUIControl.h" +#include "GUIColorManager.h" +#include "utils/CharsetConverter.h" +#include "StringUtils.h" + +using namespace std; + +#define WORK_AROUND_NEEDED_FOR_LINE_BREAKS + +CGUIString::CGUIString(iString start, iString end, bool carriageReturn) +{ + m_text.assign(start, end); + m_carriageReturn = carriageReturn; +} + +CStdString CGUIString::GetAsString() const +{ + CStdString text; + for (unsigned int i = 0; i < m_text.size(); i++) + text += (char)(m_text[i] & 0xff); + return text; +} + +CGUITextLayout::CGUITextLayout(CGUIFont *font, bool wrap, float fHeight) +{ + m_font = font; + m_textColor = 0; + m_wrap = wrap; + m_maxHeight = fHeight; + m_textWidth = 0; + m_textHeight = 0; +} + +void CGUITextLayout::SetWrap(bool bWrap) +{ + m_wrap = bWrap; +} + +void CGUITextLayout::Render(float x, float y, float angle, color_t color, color_t shadowColor, uint32_t alignment, float maxWidth, bool solid) +{ + if (!m_font) + return; + + // set the main text color + if (m_colors.size()) + m_colors[0] = color; + + // render the text at the required location, angle, and size + if (angle) + { + static const float degrees_to_radians = 0.01745329252f; + g_graphicsContext.AddTransform(TransformMatrix::CreateZRotation(angle * degrees_to_radians, x, y, g_graphicsContext.GetScalingPixelRatio())); + } + // center our text vertically + if (alignment & XBFONT_CENTER_Y) + { + y -= m_font->GetTextHeight(m_lines.size()) * 0.5f;; + alignment &= ~XBFONT_CENTER_Y; + } + m_font->Begin(); + for (vector<CGUIString>::iterator i = m_lines.begin(); i != m_lines.end(); i++) + { + const CGUIString &string = *i; + uint32_t align = alignment; + if (align & XBFONT_JUSTIFIED && string.m_carriageReturn) + align &= ~XBFONT_JUSTIFIED; + if (solid) + m_font->DrawText(x, y, m_colors[0], shadowColor, string.m_text, align, maxWidth); + else + m_font->DrawText(x, y, m_colors, shadowColor, string.m_text, align, maxWidth); + y += m_font->GetLineHeight(); + } + m_font->End(); + if (angle) + g_graphicsContext.RemoveTransform(); +} + + +void CGUITextLayout::RenderScrolling(float x, float y, float angle, color_t color, color_t shadowColor, uint32_t alignment, float maxWidth, CScrollInfo &scrollInfo) +{ + if (!m_font) + return; + + // set the main text color + if (m_colors.size()) + m_colors[0] = color; + + // render the text at the required location, angle, and size + if (angle) + { + static const float degrees_to_radians = 0.01745329252f; + g_graphicsContext.AddTransform(TransformMatrix::CreateZRotation(angle * degrees_to_radians, x, y, g_graphicsContext.GetScalingPixelRatio())); + } + // center our text vertically + if (alignment & XBFONT_CENTER_Y) + { + y -= m_font->GetTextHeight(m_lines.size()) * 0.5f;; + alignment &= ~XBFONT_CENTER_Y; + } + m_font->Begin(); + // NOTE: This workaround is needed as otherwise multi-line text that scrolls + // will scroll in proportion to the number of lines. Ideally we should + // do the DrawScrollingText calculation here. This probably won't make + // any difference to the smoothness of scrolling though which will be + // jumpy with this sort of thing. It's not exactly a well used situation + // though, so this hack is probably OK. + float speed = scrollInfo.pixelSpeed; + for (vector<CGUIString>::iterator i = m_lines.begin(); i != m_lines.end(); i++) + { + const CGUIString &string = *i; + m_font->DrawScrollingText(x, y, m_colors, shadowColor, string.m_text, alignment, maxWidth, scrollInfo); + y += m_font->GetLineHeight(); + scrollInfo.pixelSpeed = 0; + } + scrollInfo.pixelSpeed = speed; + m_font->End(); + if (angle) + g_graphicsContext.RemoveTransform(); +} + +void CGUITextLayout::RenderOutline(float x, float y, color_t color, color_t outlineColor, uint32_t outlineWidth, uint32_t alignment, float maxWidth) +{ + if (!m_font) + return; + + // set the main text color + if (m_colors.size()) + m_colors[0] = color; + + // center our text vertically + if (alignment & XBFONT_CENTER_Y) + { + y -= m_font->GetTextHeight(m_lines.size()) * 0.5f;; + alignment &= ~XBFONT_CENTER_Y; + } + m_font->Begin(); + for (vector<CGUIString>::iterator i = m_lines.begin(); i != m_lines.end(); i++) + { + const CGUIString &string = *i; + uint32_t align = alignment; + if (align & XBFONT_JUSTIFIED && string.m_carriageReturn) + align &= ~XBFONT_JUSTIFIED; + + DrawOutlineText(m_font, x, y, m_colors, outlineColor, outlineWidth, string.m_text, align, maxWidth); + y += m_font->GetLineHeight(); + } + m_font->End(); +} + +bool CGUITextLayout::Update(const CStdString &text, float maxWidth, bool forceUpdate /*= false*/, bool forceLTRReadingOrder /*= false*/) +{ + if (text == m_lastText && !forceUpdate) + return false; + + // convert to utf16 + CStdStringW utf16; + utf8ToW(text, utf16); + + // update + SetText(utf16, maxWidth, forceLTRReadingOrder); + + // and set our parameters to indicate no further update is required + m_lastText = text; + return true; +} + +void CGUITextLayout::SetText(const CStdStringW &text, float maxWidth, bool forceLTRReadingOrder /*= false*/) +{ + vecText parsedText; + + // empty out our previous string + m_lines.clear(); + m_colors.clear(); + m_colors.push_back(m_textColor); + + // parse the text into our string objects + ParseText(text, parsedText); + + // add \n to the end of the string + parsedText.push_back(L'\n'); + + // if we need to wrap the text, then do so + if (m_wrap && maxWidth > 0) + WrapText(parsedText, maxWidth); + else + LineBreakText(parsedText, m_lines); + + // remove any trailing blank lines + while (!m_lines.empty() && m_lines.back().m_text.empty()) + m_lines.pop_back(); + + BidiTransform(m_lines, forceLTRReadingOrder); + + // and cache the width and height for later reading + CalcTextExtent(); +} + +// BidiTransform is used to handle RTL text flipping in the string +void CGUITextLayout::BidiTransform(vector<CGUIString> &lines, bool forceLTRReadingOrder) +{ + for (unsigned int i=0; i<lines.size(); i++) + { + CGUIString &line = lines[i]; + + // reserve enough space in the flipped text + vecText flippedText; + flippedText.reserve(line.m_text.size()); + + character_t sectionStyle = 0xffff0000; // impossible to achieve + CStdStringW sectionText; + for (vecText::iterator it = line.m_text.begin(); it != line.m_text.end(); ++it) + { + character_t style = *it & 0xffff0000; + if (style != sectionStyle) + { + if (!sectionText.IsEmpty()) + { // style has changed, bidi flip text + CStdStringW sectionFlipped = BidiFlip(sectionText, forceLTRReadingOrder); + for (unsigned int j = 0; j < sectionFlipped.size(); j++) + flippedText.push_back(sectionStyle | sectionFlipped[j]); + } + sectionStyle = style; + sectionText.clear(); + } + sectionText.push_back( (wchar_t)(*it & 0xffff) ); + } + + // handle the last section + if (!sectionText.IsEmpty()) + { + CStdStringW sectionFlipped = BidiFlip(sectionText, forceLTRReadingOrder); + for (unsigned int j = 0; j < sectionFlipped.size(); j++) + flippedText.push_back(sectionStyle | sectionFlipped[j]); + } + + // replace the original line with the proccessed one + lines[i] = CGUIString(flippedText.begin(), flippedText.end(), line.m_carriageReturn); + } +} + +CStdStringW CGUITextLayout::BidiFlip(const CStdStringW &text, bool forceLTRReadingOrder) +{ + CStdStringA utf8text; + CStdStringW visualText; + + // convert to utf8, and back to utf16 with bidi flipping + g_charsetConverter.wToUTF8(text, utf8text); + g_charsetConverter.utf8ToW(utf8text, visualText, true, forceLTRReadingOrder); + + return visualText; +} + +void CGUITextLayout::Filter(CStdString &text) +{ + CStdStringW utf16; + utf8ToW(text, utf16); + vecColors colors; + vecText parsedText; + ParseText(utf16, 0, colors, parsedText); + utf16.Empty(); + for (unsigned int i = 0; i < parsedText.size(); i++) + utf16 += (wchar_t)(0xffff & parsedText[i]); + g_charsetConverter.wToUTF8(utf16, text); +} + +void CGUITextLayout::ParseText(const CStdStringW &text, vecText &parsedText) +{ + if (!m_font) + return; + ParseText(text, m_font->GetStyle(), m_colors, parsedText); +} + +void CGUITextLayout::ParseText(const CStdStringW &text, uint32_t defaultStyle, vecColors &colors, vecText &parsedText) +{ + // run through the string, searching for: + // [B] or [/B] -> toggle bold on and off + // [I] or [/I] -> toggle italics on and off + // [COLOR ffab007f] or [/COLOR] -> toggle color on and off + // [CAPS <option>] or [/CAPS] -> toggle capatilization on and off + + uint32_t currentStyle = defaultStyle; // start with the default font's style + color_t currentColor = 0; + + stack<color_t> colorStack; + colorStack.push(0); + + // these aren't independent, but that's probably not too much of an issue + // eg [UPPERCASE]Glah[LOWERCASE]FReD[/LOWERCASE]Georeg[/UPPERCASE] will work (lower case >> upper case) + // but [LOWERCASE]Glah[UPPERCASE]FReD[/UPPERCASE]Georeg[/LOWERCASE] won't +#define FONT_STYLE_UPPERCASE 4 +#define FONT_STYLE_LOWERCASE 8 + + int startPos = 0; + size_t pos = text.Find(L'['); + while (pos != CStdString::npos && pos + 1 < text.size()) + { + uint32_t newStyle = 0; + color_t newColor = currentColor; + bool newLine = false; + // have a [ - check if it's an ON or OFF switch + bool on(true); + int endPos = pos++; // finish of string + if (text[pos] == L'/') + { + on = false; + pos++; + } + // check for each type + if (text.Mid(pos,2) == L"B]") + { // bold - finish the current text block and assign the bold state + pos += 2; + if ((on && text.Find(L"[/B]",pos) >= 0) || // check for a matching end point + (!on && (currentStyle & FONT_STYLE_BOLD))) // or matching start point + newStyle = FONT_STYLE_BOLD; + } + else if (text.Mid(pos,2) == L"I]") + { // italics + pos += 2; + if ((on && text.Find(L"[/I]",pos) >= 0) || // check for a matching end point + (!on && (currentStyle & FONT_STYLE_ITALICS))) // or matching start point + newStyle = FONT_STYLE_ITALICS; + } + else if (text.Mid(pos,10) == L"UPPERCASE]") + { + pos += 10; + if ((on && text.Find(L"[/UPPERCASE]",pos) >= 0) || // check for a matching end point + (!on && (currentStyle & FONT_STYLE_UPPERCASE))) // or matching start point + newStyle = FONT_STYLE_UPPERCASE; + } + else if (text.Mid(pos,10) == L"LOWERCASE]") + { + pos += 10; + if ((on && text.Find(L"[/LOWERCASE]",pos) >= 0) || // check for a matching end point + (!on && (currentStyle & FONT_STYLE_LOWERCASE))) // or matching start point + newStyle = FONT_STYLE_LOWERCASE; + } + else if (text.Mid(pos,3) == L"CR]" && on) + { + newLine = true; + pos += 3; + } + else if (text.Mid(pos,5) == L"COLOR") + { // color + size_t finish = text.Find(L']', pos + 5); + if (on && finish != CStdString::npos && (size_t)text.Find(L"[/COLOR]",finish) != CStdString::npos) + { // create new color + newColor = colors.size(); + colors.push_back(g_colorManager.GetColor(text.Mid(pos + 5, finish - pos - 5))); + colorStack.push(newColor); + } + else if (!on && finish == pos + 5 && colorStack.size() > 1) + { // revert to previous color + colorStack.pop(); + newColor = colorStack.top(); + } + pos = finish + 1; + } + + if (newStyle || newColor != currentColor || newLine) + { // we have a new style or a new color, so format up the previous segment + CStdStringW subText = text.Mid(startPos, endPos - startPos); + if (currentStyle & FONT_STYLE_UPPERCASE) + subText.ToUpper(); + if (currentStyle & FONT_STYLE_LOWERCASE) + subText.ToLower(); + AppendToUTF32(subText, ((currentStyle & 3) << 24) | (currentColor << 16), parsedText); + if (newLine) + parsedText.push_back(L'\n'); + + // and switch to the new style + startPos = pos; + currentColor = newColor; + if (on) + currentStyle |= newStyle; + else + currentStyle &= ~newStyle; + } + pos = text.Find(L'[',pos); + } + // now grab the remainder of the string + CStdStringW subText = text.Mid(startPos, text.GetLength() - startPos); + if (currentStyle & FONT_STYLE_UPPERCASE) + subText.ToUpper(); + if (currentStyle & FONT_STYLE_LOWERCASE) + subText.ToLower(); + AppendToUTF32(subText, ((currentStyle & 3) << 24) | (currentColor << 16), parsedText); +} + +void CGUITextLayout::SetMaxHeight(float fHeight) +{ + m_maxHeight = fHeight; +} + +void CGUITextLayout::WrapText(const vecText &text, float maxWidth) +{ + if (!m_font) + return; + + int nMaxLines = (m_maxHeight > 0 && m_font->GetLineHeight() > 0)?(int)(m_maxHeight / m_font->GetLineHeight()):-1; + + m_lines.clear(); + + vector<CGUIString> lines; + LineBreakText(text, lines); + + for (unsigned int i = 0; i < lines.size(); i++) + { + const CGUIString &line = lines[i]; + vecText::const_iterator lastSpace = line.m_text.begin(); + vecText::const_iterator pos = line.m_text.begin(); + unsigned int lastSpaceInLine = 0; + vecText curLine; + while (pos != line.m_text.end() && (nMaxLines <= 0 || m_lines.size() <= (size_t)nMaxLines)) + { + // Get the current letter in the string + character_t letter = *pos; + // check for a space + if (CanWrapAtLetter(letter)) + { + float width = m_font->GetTextWidth(curLine); + if (width > maxWidth) + { + if (lastSpace != line.m_text.begin() && lastSpaceInLine > 0) + { + CGUIString string(curLine.begin(), curLine.begin() + lastSpaceInLine, false); + m_lines.push_back(string); + if (IsSpace(letter)) + lastSpace++; // ignore the space + pos = lastSpace; + curLine.clear(); + lastSpaceInLine = 0; + lastSpace = line.m_text.begin(); + continue; + } + } + // only add spaces if we're not empty + if (!IsSpace(letter) || curLine.size()) + { + lastSpace = pos; + lastSpaceInLine = curLine.size(); + curLine.push_back(letter); + } + } + else + curLine.push_back(letter); + pos++; + } + // now add whatever we have left to the string + float width = m_font->GetTextWidth(curLine); + if (width > maxWidth) + { + // too long - put up to the last space on if we can + remove it from what's left. + if (lastSpace != line.m_text.begin() && lastSpaceInLine > 0) + { + CGUIString string(curLine.begin(), curLine.begin() + lastSpaceInLine, false); + m_lines.push_back(string); + curLine.erase(curLine.begin(), curLine.begin() + lastSpaceInLine); + while (curLine.size() && IsSpace(curLine.at(0))) + curLine.erase(curLine.begin()); + } + } + CGUIString string(curLine.begin(), curLine.end(), true); + m_lines.push_back(string); + } +} + +void CGUITextLayout::LineBreakText(const vecText &text, vector<CGUIString> &lines) +{ + vecText::const_iterator lineStart = text.begin(); + vecText::const_iterator pos = text.begin(); + while (pos != text.end()) + { + // Get the current letter in the string + character_t letter = *pos; + + // Handle the newline character + if ((letter & 0xffff) == L'\n' ) + { // push back everything up till now + CGUIString string(lineStart, pos, true); + lines.push_back(string); + lineStart = pos + 1; + } + pos++; + } +} + +void CGUITextLayout::GetTextExtent(float &width, float &height) const +{ + width = m_textWidth; + height = m_textHeight; +} + +void CGUITextLayout::CalcTextExtent() +{ + m_textWidth = 0; + m_textHeight = 0; + if (!m_font) return; + + for (vector<CGUIString>::iterator i = m_lines.begin(); i != m_lines.end(); i++) + { + const CGUIString &string = *i; + float w = m_font->GetTextWidth(string.m_text); + if (w > m_textWidth) + m_textWidth = w; + } + m_textHeight = m_font->GetTextHeight(m_lines.size()); +} + +unsigned int CGUITextLayout::GetTextLength() const +{ + unsigned int length = 0; + for (vector<CGUIString>::const_iterator i = m_lines.begin(); i != m_lines.end(); i++) + length += i->m_text.size(); + return length; +} + +void CGUITextLayout::GetFirstText(vecText &text) const +{ + text.clear(); + if (m_lines.size()) + text = m_lines[0].m_text; +} + +float CGUITextLayout::GetTextWidth(const CStdStringW &text) const +{ + // NOTE: Assumes a single line of text + if (!m_font) return 0; + vecText utf32; + AppendToUTF32(text, (m_font->GetStyle() & 3) << 24, utf32); + return m_font->GetTextWidth(utf32); +} + +void CGUITextLayout::DrawText(CGUIFont *font, float x, float y, color_t color, color_t shadowColor, const CStdString &text, uint32_t align) +{ + if (!font) return; + vecText utf32; + AppendToUTF32(text, 0, utf32); + font->DrawText(x, y, color, shadowColor, utf32, align, 0); +} + +void CGUITextLayout::DrawOutlineText(CGUIFont *font, float x, float y, color_t color, color_t outlineColor, uint32_t outlineWidth, const CStdString &text) +{ + if (!font) return; + vecText utf32; + AppendToUTF32(text, 0, utf32); + vecColors colors; + colors.push_back(color); + DrawOutlineText(font, x, y, colors, outlineColor, outlineWidth, utf32, 0, 0); +} + +void CGUITextLayout::DrawOutlineText(CGUIFont *font, float x, float y, const vecColors &colors, color_t outlineColor, uint32_t outlineWidth, const vecText &text, uint32_t align, float maxWidth) +{ + for (unsigned int i = 1; i < outlineWidth; i++) + { + unsigned int ymax = (unsigned int)(sqrt((float)outlineWidth*outlineWidth - i*i) + 0.5f); + for (unsigned int j = 1; j < ymax; j++) + { + font->DrawText(x - i, y + j, outlineColor, 0, text, align, maxWidth); + font->DrawText(x - i, y - j, outlineColor, 0, text, align, maxWidth); + font->DrawText(x + i, y + j, outlineColor, 0, text, align, maxWidth); + font->DrawText(x + i, y - j, outlineColor, 0, text, align, maxWidth); + } + } + font->DrawText(x, y, colors, 0, text, align, maxWidth); +} + +void CGUITextLayout::AppendToUTF32(const CStdStringW &utf16, character_t colStyle, vecText &utf32) +{ + // NOTE: Assumes a single line of text + utf32.reserve(utf32.size() + utf16.size()); + for (unsigned int i = 0; i < utf16.size(); i++) + utf32.push_back(utf16[i] | colStyle); +} + +void CGUITextLayout::utf8ToW(const CStdString &utf8, CStdStringW &utf16) +{ +#ifdef WORK_AROUND_NEEDED_FOR_LINE_BREAKS + // NOTE: This appears to strip \n characters from text. This may be a consequence of incorrect + // expression of the \n in utf8 (we just use character code 10) or it might be something + // more sinister. For now, we use the workaround below. + CStdStringArray multiLines; + StringUtils::SplitString(utf8, "\n", multiLines); + for (unsigned int i = 0; i < multiLines.size(); i++) + { + CStdStringW line; + // no need to bidiflip here - it's done in BidiTransform above + g_charsetConverter.utf8ToW(multiLines[i], line, false); + utf16 += line; + if (i < multiLines.size() - 1) + utf16.push_back(L'\n'); + } +#else + // no need to bidiflip here - it's done in BidiTransform above + g_charsetConverter.utf8ToW(utf8, utf16, false); +#endif +} + +void CGUITextLayout::AppendToUTF32(const CStdString &utf8, character_t colStyle, vecText &utf32) +{ + CStdStringW utf16; + utf8ToW(utf8, utf16); + AppendToUTF32(utf16, colStyle, utf32); +} + +void CGUITextLayout::Reset() +{ + m_lines.clear(); + m_lastText.Empty(); + m_textWidth = m_textHeight = 0; +} + + diff --git a/guilib/GUITextLayout.h b/guilib/GUITextLayout.h new file mode 100644 index 0000000000..2633731637 --- /dev/null +++ b/guilib/GUITextLayout.h @@ -0,0 +1,132 @@ +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "StdString.h" + +#include <vector> + +#ifdef __GNUC__ +// under gcc, inline will only take place if optimizations are applied (-O). this will force inline even without optimizations. +#define XBMC_FORCE_INLINE __attribute__((always_inline)) +#else +#define XBMC_FORCE_INLINE +#endif + +class CGUIFont; +class CScrollInfo; + +// Process will be: + +// 1. String is divided up into a "multiinfo" vector via the infomanager. +// 2. The multiinfo vector is then parsed by the infomanager at rendertime and the resultant string is constructed. +// 3. This is saved for comparison perhaps. If the same, we are done. If not, go to 4. +// 4. The string is then parsed into a vector<CGUIString>. +// 5. Each item in the vector is length-calculated, and then layout occurs governed by alignment and wrapping rules. +// 6. A new vector<CGUIString> is constructed + +typedef uint32_t character_t; +typedef uint32_t color_t; +typedef std::vector<character_t> vecText; +typedef std::vector<color_t> vecColors; + +class CGUIString +{ +public: + typedef vecText::const_iterator iString; + + CGUIString(iString start, iString end, bool carriageReturn); + + CStdString GetAsString() const; + + vecText m_text; + bool m_carriageReturn; // true if we have a carriage return here +}; + +class CGUITextLayout +{ +public: + CGUITextLayout(CGUIFont *font, bool wrap, float fHeight=0.0f); // this may need changing - we may just use this class to replace CLabelInfo completely + + // main function to render strings + void Render(float x, float y, float angle, color_t color, color_t shadowColor, uint32_t alignment, float maxWidth, bool solid = false); + void RenderScrolling(float x, float y, float angle, color_t color, color_t shadowColor, uint32_t alignment, float maxWidth, CScrollInfo &scrollInfo); + void RenderOutline(float x, float y, color_t color, color_t outlineColor, uint32_t outlineWidth, uint32_t alignment, float maxWidth); + void GetTextExtent(float &width, float &height) const; + float GetTextWidth() const { return m_textWidth; }; + float GetTextWidth(const CStdStringW &text) const; + bool Update(const CStdString &text, float maxWidth = 0, bool forceUpdate = false, bool forceLTRReadingOrder = false); + void SetText(const CStdStringW &text, float maxWidth = 0, bool forceLTRReadingOrder = false); + + unsigned int GetTextLength() const; + void GetFirstText(vecText &text) const; + void Reset(); + + void SetWrap(bool bWrap=true); + void SetMaxHeight(float fHeight); + + + static void DrawText(CGUIFont *font, float x, float y, color_t color, color_t shadowColor, const CStdString &text, uint32_t align); + static void DrawOutlineText(CGUIFont *font, float x, float y, color_t color, color_t outlineColor, uint32_t outlineWidth, const CStdString &text); + static void Filter(CStdString &text); + +protected: + void ParseText(const CStdStringW &text, vecText &parsedText); + void LineBreakText(const vecText &text, std::vector<CGUIString> &lines); + void WrapText(const vecText &text, float maxWidth); + void BidiTransform(std::vector<CGUIString> &lines, bool forceLTRReadingOrder); + CStdStringW BidiFlip(const CStdStringW &text, bool forceLTRReadingOrder); + void CalcTextExtent(); + + // our text to render + vecColors m_colors; + std::vector<CGUIString> m_lines; + typedef std::vector<CGUIString>::iterator iLine; + + // the layout and font details + CGUIFont *m_font; // has style, colour info + bool m_wrap; // wrapping (true if justify is enabled!) + float m_maxHeight; + // the default color (may differ from the font objects defaults) + color_t m_textColor; + + CStdString m_lastText; + float m_textWidth; + float m_textHeight; +private: + inline bool IsSpace(character_t letter) const XBMC_FORCE_INLINE + { + return (letter & 0xffff) == L' '; + }; + inline bool CanWrapAtLetter(character_t letter) const XBMC_FORCE_INLINE + { + character_t ch = letter & 0xffff; + return ch == L' ' || (ch >=0x4e00 && ch <= 0x9fff); + }; + static void AppendToUTF32(const CStdString &utf8, character_t colStyle, vecText &utf32); + static void AppendToUTF32(const CStdStringW &utf16, character_t colStyle, vecText &utf32); + static void DrawOutlineText(CGUIFont *font, float x, float y, const vecColors &colors, color_t outlineColor, uint32_t outlineWidth, const vecText &text, uint32_t align, float maxWidth); + static void ParseText(const CStdStringW &text, uint32_t defaultStyle, vecColors &colors, vecText &parsedText); + + static void utf8ToW(const CStdString &utf8, CStdStringW &utf16); +}; + diff --git a/guilib/GUITexture.cpp b/guilib/GUITexture.cpp new file mode 100644 index 0000000000..adb8877f40 --- /dev/null +++ b/guilib/GUITexture.cpp @@ -0,0 +1,600 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUITexture.h" +#include "GraphicContext.h" +#include "TextureManager.h" +#include "GUILargeTextureManager.h" +#include "MathUtils.h" + +using namespace std; + +CGUITextureBase::CGUITextureBase(float posX, float posY, float width, float height, const CTextureInfo& texture) +{ + m_posX = posX; + m_posY = posY; + m_width = width; + m_height = height; + m_info = texture; + + // defaults + m_visible = true; + m_diffuseColor = 0xffffffff; + m_alpha = 0xff; + + m_vertex.SetRect(m_posX, m_posY, m_posX + m_width, m_posY + m_height); + + m_frameWidth = 0; + m_frameHeight = 0; + + m_texCoordsScaleU = 1.0f; + m_texCoordsScaleV = 1.0f; + m_diffuseU = 1.0f; + m_diffuseV = 1.0f; + m_diffuseScaleU = 1.0f; + m_diffuseScaleV = 1.0f; + m_largeOrientation = 0; + + // anim gifs + m_currentFrame = 0; + m_frameCounter = (DWORD) -1; + m_currentLoop = 0; + + m_allocateDynamically = false; + m_isAllocated = NO; + m_invalid = true; +} + +CGUITextureBase::CGUITextureBase(const CGUITextureBase &right) +{ + m_posX = right.m_posX; + m_posY = right.m_posY; + m_width = right.m_width; + m_height = right.m_height; + m_info = right.m_info; + + m_visible = right.m_visible; + m_diffuseColor = right.m_diffuseColor; + m_alpha = right.m_alpha; + m_aspect = right.m_aspect; + + m_allocateDynamically = right.m_allocateDynamically; + + // defaults + m_vertex.SetRect(m_posX, m_posY, m_posX + m_width, m_posY + m_height); + + m_frameWidth = 0; + m_frameHeight = 0; + + m_texCoordsScaleU = 1.0f; + m_texCoordsScaleV = 1.0f; + m_diffuseU = 1.0f; + m_diffuseV = 1.0f; + m_diffuseScaleU = 1.0f; + m_diffuseScaleV = 1.0f; + + m_largeOrientation = 0; + + m_currentFrame = 0; + m_frameCounter = (DWORD) -1; + m_currentLoop = 0; + + m_isAllocated = NO; + m_invalid = true; +} + +CGUITextureBase::~CGUITextureBase(void) +{ +} + +void CGUITextureBase::AllocateOnDemand() +{ + if (m_visible) + { // visible, so make sure we're allocated + if (!IsAllocated() || (m_info.useLarge && !m_texture.size())) + AllocResources(); + } + else + { // hidden, so deallocate as applicable + if (m_allocateDynamically && IsAllocated()) + FreeResources(); + // reset animated textures (animgifs) + m_currentLoop = 0; + m_currentFrame = 0; + m_frameCounter = 0; + } +} + +void CGUITextureBase::Render() +{ + // check if we need to allocate our resources + AllocateOnDemand(); + + if (!m_visible || !m_texture.size()) + return; + + if (m_texture.size() > 1) + UpdateAnimFrame(); + + if (m_invalid) + CalculateSize(); + + // see if we need to clip the image + if (m_vertex.Width() > m_width || m_vertex.Height() > m_height) + { + if (!g_graphicsContext.SetClipRegion(m_posX, m_posY, m_width, m_height)) + return; + } + + // setup our renderer + Begin(); + + // compute the texture coordinates + float u1, u2, u3, v1, v2, v3; + u1 = m_info.border.left; + u2 = m_frameWidth - m_info.border.right; + u3 = m_frameWidth; + v1 = m_info.border.top; + v2 = m_frameHeight - m_info.border.bottom; + v3 = m_frameHeight; + + if (!m_texture.m_texCoordsArePixels) + { + u1 *= m_texCoordsScaleU; + u2 *= m_texCoordsScaleU; + u3 *= m_texCoordsScaleU; + v1 *= m_texCoordsScaleV; + v2 *= m_texCoordsScaleV; + v3 *= m_texCoordsScaleV; + } + + // TODO: The diffuse coloring applies to all vertices, which will + // look weird for stuff with borders, as will the -ve height/width + // for flipping + + // left segment (0,0,u1,v3) + if (m_info.border.left) + { + if (m_info.border.top) + Render(m_vertex.x1, m_vertex.y1, m_vertex.x1 + m_info.border.left, m_vertex.y1 + m_info.border.top, 0, 0, u1, v1, u3, v3); + Render(m_vertex.x1, m_vertex.y1 + m_info.border.top, m_vertex.x1 + m_info.border.left, m_vertex.y2 - m_info.border.bottom, 0, v1, u1, v2, u3, v3); + if (m_info.border.bottom) + Render(m_vertex.x1, m_vertex.y2 - m_info.border.bottom, m_vertex.x1 + m_info.border.left, m_vertex.y2, 0, v2, u1, v3, u3, v3); + } + // middle segment (u1,0,u2,v3) + if (m_info.border.top) + Render(m_vertex.x1 + m_info.border.left, m_vertex.y1, m_vertex.x2 - m_info.border.right, m_vertex.y1 + m_info.border.top, u1, 0, u2, v1, u3, v3); + Render(m_vertex.x1 + m_info.border.left, m_vertex.y1 + m_info.border.top, m_vertex.x2 - m_info.border.right, m_vertex.y2 - m_info.border.bottom, u1, v1, u2, v2, u3, v3); + if (m_info.border.bottom) + Render(m_vertex.x1 + m_info.border.left, m_vertex.y2 - m_info.border.bottom, m_vertex.x2 - m_info.border.right, m_vertex.y2, u1, v2, u2, v3, u3, v3); + // right segment + if (m_info.border.right) + { // have a left border + if (m_info.border.top) + Render(m_vertex.x2 - m_info.border.right, m_vertex.y1, m_vertex.x2, m_vertex.y1 + m_info.border.top, u2, 0, u3, v1, u3, v3); + Render(m_vertex.x2 - m_info.border.right, m_vertex.y1 + m_info.border.top, m_vertex.x2, m_vertex.y2 - m_info.border.bottom, u2, v1, u3, v2, u3, v3); + if (m_info.border.bottom) + Render(m_vertex.x2 - m_info.border.right, m_vertex.y2 - m_info.border.bottom, m_vertex.x2, m_vertex.y2, u2, v2, u3, v3, u3, v3); + } + + // close off our renderer + End(); + + if (m_vertex.Width() > m_width || m_vertex.Height() > m_height) + g_graphicsContext.RestoreClipRegion(); +} + +void CGUITextureBase::Render(float left, float top, float right, float bottom, float u1, float v1, float u2, float v2, float u3, float v3) +{ + CRect diffuse(u1, v1, u2, v2); + CRect texture(u1, v1, u2, v2); + CRect vertex(left, top, right, bottom); + g_graphicsContext.ClipRect(vertex, texture, m_diffuse.size() ? &diffuse : NULL); + + if (vertex.IsEmpty()) + return; // nothing to render + + int orientation = GetOrientation(); + OrientateTexture(texture, u3, v3, orientation); + + if (m_diffuse.size()) + { + // flip the texture as necessary. Diffuse just gets flipped according to m_info.orientation. + // Main texture gets flipped according to GetOrientation(). + diffuse.x1 *= m_diffuseScaleU / u3; diffuse.x2 *= m_diffuseScaleU / u3; + diffuse.y1 *= m_diffuseScaleV / v3; diffuse.y2 *= m_diffuseScaleV / v3; + diffuse += m_diffuseOffset; + OrientateTexture(diffuse, m_diffuseU, m_diffuseV, m_info.orientation); + } + + float x[4], y[4], z[4]; + +#define ROUND_TO_PIXEL(x) (float)(MathUtils::round_int(x)) + + x[0] = ROUND_TO_PIXEL(g_graphicsContext.ScaleFinalXCoord(vertex.x1, vertex.y1)); + y[0] = ROUND_TO_PIXEL(g_graphicsContext.ScaleFinalYCoord(vertex.x1, vertex.y1)); + z[0] = ROUND_TO_PIXEL(g_graphicsContext.ScaleFinalZCoord(vertex.x1, vertex.y1)); + x[1] = ROUND_TO_PIXEL(g_graphicsContext.ScaleFinalXCoord(vertex.x2, vertex.y1)); + y[1] = ROUND_TO_PIXEL(g_graphicsContext.ScaleFinalYCoord(vertex.x2, vertex.y1)); + z[1] = ROUND_TO_PIXEL(g_graphicsContext.ScaleFinalZCoord(vertex.x2, vertex.y1)); + x[2] = ROUND_TO_PIXEL(g_graphicsContext.ScaleFinalXCoord(vertex.x2, vertex.y2)); + y[2] = ROUND_TO_PIXEL(g_graphicsContext.ScaleFinalYCoord(vertex.x2, vertex.y2)); + z[2] = ROUND_TO_PIXEL(g_graphicsContext.ScaleFinalZCoord(vertex.x2, vertex.y2)); + x[3] = ROUND_TO_PIXEL(g_graphicsContext.ScaleFinalXCoord(vertex.x1, vertex.y2)); + y[3] = ROUND_TO_PIXEL(g_graphicsContext.ScaleFinalYCoord(vertex.x1, vertex.y2)); + z[3] = ROUND_TO_PIXEL(g_graphicsContext.ScaleFinalZCoord(vertex.x1, vertex.y2)); + + if (y[2] == y[0]) y[2] += 1.0f; if (x[2] == x[0]) x[2] += 1.0f; + if (y[3] == y[1]) y[3] += 1.0f; if (x[3] == x[1]) x[3] += 1.0f; + +#define MIX_ALPHA(a,c) (((a * (c >> 24)) / 255) << 24) | (c & 0x00ffffff) + + color_t color = m_diffuseColor; + if (m_alpha != 0xFF) color = MIX_ALPHA(m_alpha, m_diffuseColor); + color = g_graphicsContext.MergeAlpha(color); + + Draw(x, y, z, texture, diffuse, color, orientation); +} + +void CGUITextureBase::AllocResources() +{ + if (m_info.filename.IsEmpty()) + return; + + if (m_texture.size()) + return; // already have our texture + + // reset our animstate + m_frameCounter = 0; + m_currentFrame = 0; + m_currentLoop = 0; + + if (m_info.useLarge) + { // we want to use the large image loader, but we first check for bundled textures + if (!IsAllocated()) + { + int images = g_TextureManager.Load(m_info.filename, true); + if (images) + { + m_isAllocated = NORMAL; + m_texture = g_TextureManager.GetTexture(m_info.filename); + } + } + if (m_isAllocated != NORMAL) + { // use our large image background loader + CTextureArray texture; + if (g_largeTextureManager.GetImage(m_info.filename, texture, m_largeOrientation, !IsAllocated())) + { + m_isAllocated = LARGE; + + if (!texture.size()) // not ready as yet + return; + + m_texture = texture; + } + else + m_isAllocated = LARGE_FAILED; + } + } + else if (!IsAllocated()) + { + int images = g_TextureManager.Load(m_info.filename); + + // set allocated to true even if we couldn't load the image to save + // us hitting the disk every frame + m_isAllocated = images ? NORMAL : NORMAL_FAILED; + if (!images) + return; + + m_texture = g_TextureManager.GetTexture(m_info.filename); + } + m_frameWidth = (float)m_texture.m_width; + m_frameHeight = (float)m_texture.m_height; + + // load the diffuse texture (if necessary) + if (!m_info.diffuse.IsEmpty()) + { + g_TextureManager.Load(m_info.diffuse); + m_diffuse = g_TextureManager.GetTexture(m_info.diffuse); + } + + CalculateSize(); + + // call our implementation + Allocate(); +} + +void CGUITextureBase::CalculateSize() +{ + if (m_currentFrame >= m_texture.size()) + return; + + m_texCoordsScaleU = 1.0f / m_texture.m_texWidth; + m_texCoordsScaleV = 1.0f / m_texture.m_texHeight; + + if (m_width == 0) + m_width = m_frameWidth; + if (m_height == 0) + m_height = m_frameHeight; + + float newPosX = m_posX; + float newPosY = m_posY; + float newWidth = m_width; + float newHeight = m_height; + + if (m_aspect.ratio != CAspectRatio::AR_STRETCH && m_frameWidth && m_frameHeight) + { + // to get the pixel ratio, we must use the SCALED output sizes + float pixelRatio = g_graphicsContext.GetScalingPixelRatio(); + + float fSourceFrameRatio = m_frameWidth / m_frameHeight; + if (GetOrientation() & 4) + fSourceFrameRatio = m_frameHeight / m_frameWidth; + float fOutputFrameRatio = fSourceFrameRatio / pixelRatio; + + // maximize the width + newHeight = m_width / fOutputFrameRatio; + + if ((m_aspect.ratio == CAspectRatio::AR_SCALE && newHeight < m_height) || + (m_aspect.ratio == CAspectRatio::AR_KEEP && newHeight > m_height)) + { + newHeight = m_height; + newWidth = newHeight * fOutputFrameRatio; + } + if (m_aspect.ratio == CAspectRatio::AR_CENTER) + { // keep original size + center + newWidth = m_frameWidth; + newHeight = m_frameHeight; + } + + if (m_aspect.align & ASPECT_ALIGN_LEFT) + newPosX = m_posX; + else if (m_aspect.align & ASPECT_ALIGN_RIGHT) + newPosX = m_posX + m_width - newWidth; + else + newPosX = m_posX + (m_width - newWidth) * 0.5f; + if (m_aspect.align & ASPECT_ALIGNY_TOP) + newPosY = m_posY; + else if (m_aspect.align & ASPECT_ALIGNY_BOTTOM) + newPosY = m_posY + m_height - newHeight; + else + newPosY = m_posY + (m_height - newHeight) * 0.5f; + } + m_vertex.SetRect(newPosX, newPosY, newPosX + newWidth, newPosY + newHeight); + + // scale the diffuse coords as well + if (m_diffuse.size()) + { // calculate scaling for the texcoords + if (m_diffuse.m_texCoordsArePixels) + { + m_diffuseU = float(m_diffuse.m_width); + m_diffuseV = float(m_diffuse.m_height); + } + else + { + m_diffuseU = float(m_diffuse.m_width) / float(m_diffuse.m_texWidth); + m_diffuseV = float(m_diffuse.m_height) / float(m_diffuse.m_texHeight); + } + + if (m_aspect.scaleDiffuse) + { + m_diffuseScaleU = m_diffuseU; + m_diffuseScaleV = m_diffuseV; + m_diffuseOffset = CPoint(0,0); + } + else // stretch'ing diffuse + { // scale diffuse up or down to match output rect size, rather than image size + //(m_fX, mfY) -> (m_fX + m_fNW, m_fY + m_fNH) + //(0,0) -> (m_fU*m_diffuseScaleU, m_fV*m_diffuseScaleV) + // x = u/(m_fU*m_diffuseScaleU)*m_fNW + m_fX + // -> u = (m_posX - m_fX) * m_fU * m_diffuseScaleU / m_fNW + m_diffuseScaleU = m_diffuseU * m_vertex.Width() / m_width; + m_diffuseScaleV = m_diffuseV * m_vertex.Height() / m_height; + m_diffuseOffset = CPoint((m_vertex.x1 - m_posX) / m_vertex.Width() * m_diffuseScaleU, (m_vertex.y1 - m_posY) / m_vertex.Height() * m_diffuseScaleV); + } + } + m_invalid = false; +} + +void CGUITextureBase::FreeResources(bool immediately /* = false */) +{ + if (m_isAllocated == LARGE || m_isAllocated == LARGE_FAILED) + g_largeTextureManager.ReleaseImage(m_info.filename, immediately); + else if (m_isAllocated == NORMAL && m_texture.size()) + g_TextureManager.ReleaseTexture(m_info.filename); + + if (m_diffuse.size()) + g_TextureManager.ReleaseTexture(m_info.diffuse); + m_diffuse.Reset(); + + m_texture.Reset(); + + m_currentFrame = 0; + m_currentLoop = 0; + m_texCoordsScaleU = 1.0f; + m_texCoordsScaleV = 1.0f; + m_largeOrientation = 0; + + // call our implementation + Free(); + + m_isAllocated = NO; +} + +void CGUITextureBase::DynamicResourceAlloc(bool allocateDynamically) +{ + m_allocateDynamically = allocateDynamically; +} + +void CGUITextureBase::UpdateAnimFrame() +{ + m_frameCounter++; + DWORD delay = m_texture.m_delays[m_currentFrame]; + if (!delay) delay = 100; + if (m_frameCounter * 40 >= delay) + { + m_frameCounter = 0; + if (m_currentFrame + 1 >= m_texture.size()) + { + if (m_texture.m_loops > 0) + { + if (m_currentLoop + 1 < m_texture.m_loops) + { + m_currentLoop++; + m_currentFrame = 0; + } + } + else + { + // 0 == loop forever + m_currentFrame = 0; + } + } + else + { + m_currentFrame++; + } + } +} + +void CGUITextureBase::SetVisible(bool visible) +{ + m_visible = visible; +} + +void CGUITextureBase::SetAlpha(unsigned char alpha) +{ + m_alpha = alpha; +} + +void CGUITextureBase::SetDiffuseColor(color_t color) +{ + m_diffuseColor = color; +} + +bool CGUITextureBase::ReadyToRender() const +{ + return m_texture.size() > 0; +} + +void CGUITextureBase::OrientateTexture(CRect &rect, float width, float height, int orientation) +{ + switch (orientation & 3) + { + case 0: + // default + break; + case 1: + // flip in X direction + rect.x1 = width - rect.x1; + rect.x2 = width - rect.x2; + break; + case 2: + // rotate 180 degrees + rect.x1 = width - rect.x1; + rect.x2 = width - rect.x2; + rect.y1 = height - rect.y1; + rect.y2 = height - rect.y2; + break; + case 3: + // flip in Y direction + rect.y1 = height - rect.y1; + rect.y2 = height - rect.y2; + break; + } + if (orientation & 4) + { + // we need to swap x and y coordinates but only within the width,height block + float temp = rect.x1; + rect.x1 = rect.y1 * width/height; + rect.y1 = temp * height/width; + temp = rect.x2; + rect.x2 = rect.y2 * width/height; + rect.y2 = temp * height/width; + } +} + +void CGUITextureBase::SetWidth(float width) +{ + if (width < m_info.border.left + m_info.border.right) + width = m_info.border.left + m_info.border.right; + if (m_width != width) + { + m_width = width; + m_invalid = true; + } +} + +void CGUITextureBase::SetHeight(float height) +{ + if (height < m_info.border.top + m_info.border.bottom) + height = m_info.border.top + m_info.border.bottom; + if (m_height != height) + { + m_height = height; + m_invalid = true; + } +} + +void CGUITextureBase::SetPosition(float posX, float posY) +{ + if (m_posX != posX || m_posY != posY) + { + m_posX = posX; + m_posY = posY; + m_invalid = true; + } +} + +void CGUITextureBase::SetAspectRatio(const CAspectRatio &aspect) +{ + if (m_aspect != aspect) + { + m_aspect = aspect; + m_invalid = true; + } +} + +void CGUITextureBase::SetFileName(const CStdString& filename) +{ + if (m_info.filename.Equals(filename)) return; + // Don't completely free resources here - we may be just changing + // filenames mid-animation + FreeResources(); + m_info.filename = filename; + // Don't allocate resources here as this is done at render time +} + +int CGUITextureBase::GetOrientation() const +{ + if (m_isAllocated == LARGE) + return m_info.orientation; + // otherwise multiply our orientations + static char orient_table[] = { 0, 1, 2, 3, 4, 5, 6, 7, + 1, 0, 3, 2, 5, 4, 7, 6, + 2, 3, 0, 1, 6, 7, 4, 5, + 3, 2, 1, 0, 7, 6, 5, 4, + 4, 7, 6, 5, 0, 3, 2, 1, + 5, 6, 7, 4, 1, 2, 3, 0, + 6, 5, 4, 7, 2, 1, 0, 3, + 7, 4, 5, 6, 3, 0, 1, 2 }; + return (int)orient_table[8 * m_info.orientation + m_largeOrientation]; +} diff --git a/guilib/GUITexture.h b/guilib/GUITexture.h new file mode 100644 index 0000000000..91816a52a5 --- /dev/null +++ b/guilib/GUITexture.h @@ -0,0 +1,215 @@ +/*! +\file GUITexture.h +\brief +*/ + +#ifndef GUILIB_GUITEXTURE_H +#define GUILIB_GUITEXTURE_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "TextureManager.h" +#include "Geometry.h" + +typedef uint32_t color_t; + +struct FRECT +{ + float left; + float top; + float right; + float bottom; +}; + +// image alignment for <aspect>keep</aspect>, <aspect>scale</aspect> or <aspect>center</aspect> +#define ASPECT_ALIGN_CENTER 0 +#define ASPECT_ALIGN_LEFT 1 +#define ASPECT_ALIGN_RIGHT 2 +#define ASPECT_ALIGNY_CENTER 0 +#define ASPECT_ALIGNY_TOP 4 +#define ASPECT_ALIGNY_BOTTOM 8 +#define ASPECT_ALIGN_MASK 3 +#define ASPECT_ALIGNY_MASK ~3 + +class CAspectRatio +{ +public: + enum ASPECT_RATIO { AR_STRETCH = 0, AR_SCALE, AR_KEEP, AR_CENTER }; + CAspectRatio(ASPECT_RATIO aspect = AR_STRETCH) + { + ratio = aspect; + align = ASPECT_ALIGN_CENTER | ASPECT_ALIGNY_CENTER; + scaleDiffuse = true; + }; + bool operator!=(const CAspectRatio &right) const + { + if (ratio != right.ratio) return true; + if (align != right.align) return true; + if (scaleDiffuse != right.scaleDiffuse) return true; + return false; + }; + + ASPECT_RATIO ratio; + uint32_t align; + bool scaleDiffuse; +}; + +class CTextureInfo +{ +public: + CTextureInfo() + { + memset(&border, 0, sizeof(FRECT)); + orientation = 0; + useLarge = false; + }; + + CTextureInfo(const CStdString &file) + { + memset(&border, 0, sizeof(FRECT)); + orientation = 0; + useLarge = false; + filename = file; + } + + void operator=(const CTextureInfo &right) + { + memcpy(&border, &right.border, sizeof(FRECT)); + orientation = right.orientation; + diffuse = right.diffuse; + filename = right.filename; + useLarge = right.useLarge; + }; + bool useLarge; + FRECT border; // scaled - unneeded if we get rid of scale on load + int orientation; // orientation of the texture (0 - 7 == EXIForientation - 1) + CStdString diffuse; // diffuse overlay texture + CStdString filename; // main texture file +}; + +class CGUITextureBase +{ +public: + CGUITextureBase(float posX, float posY, float width, float height, const CTextureInfo& texture); + CGUITextureBase(const CGUITextureBase &left); + virtual ~CGUITextureBase(void); + + void Render(); + + void DynamicResourceAlloc(bool bOnOff); + void AllocResources(); + void FreeResources(bool immediately = false); + + void SetVisible(bool visible); + void SetAlpha(unsigned char alpha); + void SetDiffuseColor(color_t color); + void SetPosition(float x, float y); + void SetWidth(float width); + void SetHeight(float height); + void SetFileName(const CStdString &filename); + void SetAspectRatio(const CAspectRatio &aspect); + + const CStdString& GetFileName() const { return m_info.filename; }; + float GetTextureWidth() const { return m_frameWidth; }; + float GetTextureHeight() const { return m_frameHeight; }; + float GetWidth() const { return m_width; }; + float GetHeight() const { return m_height; }; + float GetXPosition() const { return m_posX; }; + float GetYPosition() const { return m_posY; }; + int GetOrientation() const; + const CRect &GetRenderRect() const { return m_vertex; }; + bool IsLazyLoaded() const { return m_info.useLarge; }; + + bool HitTest(const CPoint &point) const { return CRect(m_posX, m_posY, m_posX + m_width, m_posY + m_height).PtInRect(point); }; + bool IsAllocated() const { return m_isAllocated != NO; }; + bool FailedToAlloc() const { return m_isAllocated == NORMAL_FAILED || m_isAllocated == LARGE_FAILED; }; + bool ReadyToRender() const; +protected: + void CalculateSize(); + void LoadDiffuseImage(); + void AllocateOnDemand(); + void UpdateAnimFrame(); + void Render(float left, float top, float bottom, float right, float u1, float v1, float u2, float v2, float u3, float v3); + void OrientateTexture(CRect &rect, float width, float height, int orientation); + + // functions that our implementation classes handle + virtual void Allocate() {}; ///< called after our textures have been allocated + virtual void Free() {}; ///< called after our textures have been freed + virtual void Begin() {}; + virtual void Draw(float *x, float *y, float *z, const CRect &texture, const CRect &diffuse, color_t color, int orientation)=0; + virtual void End() {}; + + bool m_visible; + color_t m_diffuseColor; + + float m_posX; // size of the frame + float m_posY; + float m_width; + float m_height; + + CRect m_vertex; // vertex coords to render + bool m_invalid; // if true, we need to recalculate + + unsigned char m_alpha; + + float m_frameWidth, m_frameHeight; // size in pixels of the actual frame within the texture + float m_texCoordsScaleU, m_texCoordsScaleV; // scale factor for pixel->texture coordinates + + // animations + int m_currentLoop; + unsigned int m_currentFrame; + DWORD m_frameCounter; + + float m_diffuseU, m_diffuseV; // size of the diffuse frame (in tex coords) + float m_diffuseScaleU, m_diffuseScaleV; // scale factor of the diffuse frame (from texture coords to diffuse tex coords) + CPoint m_diffuseOffset; // offset into the diffuse frame (it's not always the origin) + + bool m_allocateDynamically; + enum ALLOCATE_TYPE { NO = 0, NORMAL, LARGE, NORMAL_FAILED, LARGE_FAILED }; + ALLOCATE_TYPE m_isAllocated; + + CTextureInfo m_info; + CAspectRatio m_aspect; + + int m_largeOrientation; // orientation for large textures + + CTextureArray m_diffuse; + CTextureArray m_texture; +}; + + +#if defined(HAS_SDL_2D) +#include "GUITextureSDL.h" +#elif defined(HAS_GL) +#include "GUITextureGL.h" +#define CGUITexture CGUITextureGL +#elif defined(HAS_GLES) +#include "GUITextureGLES.h" +#define CGUITexture CGUITextureGLES +#elif defined(HAS_DX) +#include "GUITextureD3D.h" +#define CGUITexture CGUITextureD3D +#endif + +#endif diff --git a/guilib/GUITextureD3D.cpp b/guilib/GUITextureD3D.cpp new file mode 100644 index 0000000000..27f341a284 --- /dev/null +++ b/guilib/GUITextureD3D.cpp @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "Texture.h" +#include "GUITextureD3D.h" +#include "WindowingFactory.h" + +#ifdef HAS_DX + +CGUITextureD3D::CGUITextureD3D(float posX, float posY, float width, float height, const CTextureInfo &texture) +: CGUITextureBase(posX, posY, width, height, texture) +{ +} + +void CGUITextureD3D::Begin() +{ + CBaseTexture* texture = m_texture.m_textures[m_currentFrame]; + LPDIRECT3DDEVICE9 p3DDevice = g_Windowing.Get3DDevice(); + + texture->LoadToGPU(); + if (m_diffuse.size()) + m_diffuse.m_textures[0]->LoadToGPU(); + // Set state to render the image + p3DDevice->SetTexture( 0, texture->GetTextureObject() ); + p3DDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + p3DDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + p3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE ); + p3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); + p3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); + p3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE ); + p3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE ); + p3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE ); + p3DDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP ); + p3DDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP ); + if (m_diffuse.size()) + { + p3DDevice->SetTexture( 1, m_diffuse.m_textures[0]->GetTextureObject() ); + p3DDevice->SetSamplerState( 1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); + p3DDevice->SetSamplerState( 1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR ); + p3DDevice->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE ); + p3DDevice->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_CURRENT ); + p3DDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_MODULATE ); + p3DDevice->SetTextureStageState( 1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE ); + p3DDevice->SetTextureStageState( 1, D3DTSS_ALPHAARG2, D3DTA_CURRENT ); + p3DDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP, D3DTOP_MODULATE ); + p3DDevice->SetSamplerState( 1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP ); + p3DDevice->SetSamplerState( 1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP ); + } + + p3DDevice->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE ); + p3DDevice->SetRenderState( D3DRS_ALPHAREF, 0 ); + p3DDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL ); + p3DDevice->SetRenderState( D3DRS_ZENABLE, FALSE ); + p3DDevice->SetRenderState( D3DRS_FOGENABLE, FALSE ); + p3DDevice->SetRenderState( D3DRS_FOGTABLEMODE, D3DFOG_NONE ); + p3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID ); + p3DDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ); + p3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); + p3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); + p3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); + p3DDevice->SetRenderState( D3DRS_LIGHTING, FALSE); + + p3DDevice->SetFVF( D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX2 ); +} + +void CGUITextureD3D::End() +{ + // unset the texture and palette or the texture caching crashes because the runtime still has a reference + g_Windowing.Get3DDevice()->SetTexture( 0, NULL ); + if (m_diffuse.size()) + g_Windowing.Get3DDevice()->SetTexture( 1, NULL ); +} + +void CGUITextureD3D::Draw(float *x, float *y, float *z, const CRect &texture, const CRect &diffuse, color_t color, int orientation) +{ + struct CUSTOMVERTEX { + FLOAT x, y, z; + DWORD color; + FLOAT tu, tv; // Texture coordinates + FLOAT tu2, tv2; + }; + + // D3D aligns to half pixel boundaries + for (int i = 0; i < 4; i++) + { + x[i] -= 0.5f; + y[i] -= 0.5f; + z[i] -= 0.5f; + }; + + CUSTOMVERTEX verts[4]; + verts[0].x = x[0]; verts[0].y = y[0]; verts[0].z = z[0]; + verts[0].tu = texture.x1; verts[0].tv = texture.y1; + verts[0].tu2 = diffuse.x1; verts[0].tv2 = diffuse.y1; + verts[0].color = color; + + verts[1].x = x[1]; verts[1].y = y[1]; verts[1].z = z[1]; + if (orientation & 4) + { + verts[1].tu = texture.x1; + verts[1].tv = texture.y2; + } + else + { + verts[1].tu = texture.x2; + verts[1].tv = texture.y1; + } + if (m_info.orientation & 4) + { + verts[1].tu2 = diffuse.x1; + verts[1].tv2 = diffuse.y2; + } + else + { + verts[1].tu2 = diffuse.x2; + verts[1].tv2 = diffuse.y1; + } + verts[1].color = color; + + verts[2].x = x[2]; verts[2].y = y[2]; verts[2].z = z[2]; + verts[2].tu = texture.x2; verts[2].tv = texture.y2; + verts[2].tu2 = diffuse.x2; verts[2].tv2 = diffuse.y2; + verts[2].color = color; + + verts[3].x = x[3]; verts[3].y = y[3]; verts[3].z = z[3]; + if (orientation & 4) + { + verts[3].tu = texture.x2; + verts[3].tv = texture.y1; + } + else + { + verts[3].tu = texture.x1; + verts[3].tv = texture.y2; + } + if (m_info.orientation & 4) + { + verts[3].tu2 = diffuse.x2; + verts[3].tv2 = diffuse.y1; + } + else + { + verts[3].tu2 = diffuse.x1; + verts[3].tv2 = diffuse.y2; + } + verts[3].color = color; + + g_Windowing.Get3DDevice()->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, verts, sizeof(CUSTOMVERTEX)); +} + +void CGUITextureD3D::DrawQuad(const CRect &rect, color_t color, CBaseTexture *texture, const CRect *texCoords) +{ + struct CUSTOMVERTEX { + FLOAT x, y, z; + DWORD color; + FLOAT tu, tv; // Texture coordinates + FLOAT tu2, tv2; + }; + + LPDIRECT3DDEVICE9 p3DDevice = g_Windowing.Get3DDevice(); + + if (texture) + { + texture->LoadToGPU(); + // Set state to render the image + p3DDevice->SetTexture( 0, texture->GetTextureObject() ); + p3DDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + p3DDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + p3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE ); + p3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); + p3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); + p3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE ); + p3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE ); + p3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE ); + p3DDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP ); + p3DDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP ); + } + + p3DDevice->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE ); + p3DDevice->SetRenderState( D3DRS_ALPHAREF, 0 ); + p3DDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL ); + p3DDevice->SetRenderState( D3DRS_ZENABLE, FALSE ); + p3DDevice->SetRenderState( D3DRS_FOGENABLE, FALSE ); + p3DDevice->SetRenderState( D3DRS_FOGTABLEMODE, D3DFOG_NONE ); + p3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID ); + p3DDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ); + p3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); + p3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); + p3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); + p3DDevice->SetRenderState( D3DRS_LIGHTING, FALSE); + + p3DDevice->SetFVF( D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX2 ); + + CRect coords = texCoords ? *texCoords : CRect(0.0f, 0.0f, 1.0f, 1.0f); + CUSTOMVERTEX verts[4] = { + { rect.x1 - 0.5f, rect.y1 - 0.5f, 0, color, coords.x1, coords.y1, 0, 0 }, + { rect.x2 - 0.5f, rect.y1 - 0.5f, 0, color, coords.x2, coords.y1, 0, 0 }, + { rect.x2 - 0.5f, rect.y2 - 0.5f, 0, color, coords.x2, coords.y2, 0, 0 }, + { rect.x1 - 0.5f, rect.y2 - 0.5f, 0, color, coords.x1, coords.y2, 0, 0 }, + }; + p3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, verts, sizeof(CUSTOMVERTEX)); + + p3DDevice->SetTexture( 0, NULL ); +} + +#endif
\ No newline at end of file diff --git a/guilib/GUITextureD3D.h b/guilib/GUITextureD3D.h new file mode 100644 index 0000000000..b168f685eb --- /dev/null +++ b/guilib/GUITextureD3D.h @@ -0,0 +1,49 @@ +/*! +\file GUITextureD3D.h +\brief +*/ + +#ifndef GUILIB_GUITEXTURED3D_H +#define GUILIB_GUITEXTURED3D_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUITexture.h" + +#ifdef HAS_DX + +class CGUITextureD3D : public CGUITextureBase +{ +public: + CGUITextureD3D(float posX, float posY, float width, float height, const CTextureInfo& texture); + static void DrawQuad(const CRect &coords, color_t color, CBaseTexture *texture = NULL, const CRect *texCoords = NULL); +protected: + void Begin(); + void Draw(float *x, float *y, float *z, const CRect &texture, const CRect &diffuse, color_t color, int orientation); + void End(); +}; + +#endif + +#endif
\ No newline at end of file diff --git a/guilib/GUITextureGL.cpp b/guilib/GUITextureGL.cpp new file mode 100644 index 0000000000..8af30ff291 --- /dev/null +++ b/guilib/GUITextureGL.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" +#include "GUITextureGL.h" +#include "Texture.h" +#include "utils/log.h" + +#if defined(HAS_GL) + +CGUITextureGL::CGUITextureGL(float posX, float posY, float width, float height, const CTextureInfo &texture) +: CGUITextureBase(posX, posY, width, height, texture) +{ +} + +void CGUITextureGL::Begin() +{ + CBaseTexture* texture = m_texture.m_textures[m_currentFrame]; + glActiveTextureARB(GL_TEXTURE0_ARB); + texture->LoadToGPU(); + if (m_diffuse.size()) + m_diffuse.m_textures[0]->LoadToGPU(); + + glBindTexture(GL_TEXTURE_2D, texture->GetTextureObject()); + glEnable(GL_TEXTURE_2D); + + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); // Turn Blending On + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + // diffuse coloring + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0); + glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); + glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + VerifyGLState(); + + if (m_diffuse.size()) + { + glActiveTextureARB(GL_TEXTURE1_ARB); + glBindTexture(GL_TEXTURE_2D, m_diffuse.m_textures[0]->GetTextureObject()); + glEnable(GL_TEXTURE_2D); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE1); + glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS); + glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + VerifyGLState(); + } + //glDisable(GL_TEXTURE_2D); // uncomment these 2 lines to switch to wireframe rendering + //glBegin(GL_LINE_LOOP); + glBegin(GL_QUADS); +} + +void CGUITextureGL::End() +{ + glEnd(); + if (m_diffuse.size()) + { + glDisable(GL_TEXTURE_2D); + glActiveTextureARB(GL_TEXTURE0_ARB); + } + glDisable(GL_TEXTURE_2D); +} + +void CGUITextureGL::Draw(float *x, float *y, float *z, const CRect &texture, const CRect &diffuse, color_t color, int orientation) +{ + GLubyte a = (GLubyte)GET_A(color); + GLubyte r = (GLubyte)GET_R(color); + GLubyte g = (GLubyte)GET_G(color); + GLubyte b = (GLubyte)GET_B(color); + + // Top-left vertex (corner) + glColor4ub(r, g, b, a); + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, texture.x1, texture.y1); + if (m_diffuse.size()) + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, diffuse.x1, diffuse.y1); + glVertex3f(x[0], y[0], z[0]); + + // Top-right vertex (corner) + glColor4ub(r, g, b, a); + if (orientation & 4) + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, texture.x1, texture.y2); + else + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, texture.x2, texture.y1); + if (m_diffuse.size()) + { + if (m_info.orientation & 4) + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, diffuse.x1, diffuse.y2); + else + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, diffuse.x2, diffuse.y1); + } + glVertex3f(x[1], y[1], z[1]); + + // Bottom-right vertex (corner) + glColor4ub(r, g, b, a); + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, texture.x2, texture.y2); + if (m_diffuse.size()) + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, diffuse.x2, diffuse.y2); + glVertex3f(x[2], y[2], z[2]); + + // Bottom-left vertex (corner) + glColor4ub(r, g, b, a); + if (orientation & 4) + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, texture.x2, texture.y1); + else + glMultiTexCoord2fARB(GL_TEXTURE0_ARB, texture.x1, texture.y2); + if (m_diffuse.size()) + { + if (m_info.orientation & 4) + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, diffuse.x2, diffuse.y1); + else + glMultiTexCoord2fARB(GL_TEXTURE1_ARB, diffuse.x1, diffuse.y2); + } + glVertex3f(x[3], y[3], z[3]); +} + +void CGUITextureGL::DrawQuad(const CRect &rect, color_t color, CBaseTexture *texture, const CRect *texCoords) +{ + if (texture) + { + glActiveTextureARB(GL_TEXTURE0_ARB); + texture->LoadToGPU(); + glBindTexture(GL_TEXTURE_2D, texture->GetTextureObject()); + glEnable(GL_TEXTURE_2D); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE1); + glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS); + glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + } + else + glDisable(GL_TEXTURE_2D); + + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); // Turn Blending On + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + // diffuse coloring + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0); + glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); + glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + VerifyGLState(); + + glBegin(GL_QUADS); + + glColor4ub((GLubyte)GET_R(color), (GLubyte)GET_G(color), (GLubyte)GET_B(color), (GLubyte)GET_A(color)); + + CRect coords = texCoords ? *texCoords : CRect(0.0f, 0.0f, 1.0f, 1.0f); + glTexCoord2f(coords.x1, coords.y1); + glVertex3f(rect.x1, rect.y1, 0); + glTexCoord2f(coords.x2, coords.y1); + glVertex3f(rect.x2, rect.y1, 0); + glTexCoord2f(coords.x2, coords.y2); + glVertex3f(rect.x2, rect.y2, 0); + glTexCoord2f(coords.x1, coords.y2); + glVertex3f(rect.x1, rect.y2, 0); + + glEnd(); + if (texture) + glDisable(GL_TEXTURE_2D); +} + +#endif diff --git a/guilib/GUITextureGL.h b/guilib/GUITextureGL.h new file mode 100644 index 0000000000..bf349f05dd --- /dev/null +++ b/guilib/GUITextureGL.h @@ -0,0 +1,45 @@ +/*! +\file GUITextureGL.h +\brief +*/ + +#ifndef GUILIB_GUITEXTUREGL_H +#define GUILIB_GUITEXTUREGL_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUITexture.h" + +class CGUITextureGL : public CGUITextureBase +{ +public: + CGUITextureGL(float posX, float posY, float width, float height, const CTextureInfo& texture); + static void DrawQuad(const CRect &coords, color_t color, CBaseTexture *texture = NULL, const CRect *texCoords = NULL); +protected: + void Begin(); + void Draw(float *x, float *y, float *z, const CRect &texture, const CRect &diffuse, color_t color, int orientation); + void End(); +}; + +#endif diff --git a/guilib/GUITextureGLES.cpp b/guilib/GUITextureGLES.cpp new file mode 100644 index 0000000000..7f1458e22d --- /dev/null +++ b/guilib/GUITextureGLES.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" +#include "GUITextureGLES.h" +#include "Texture.h" + +#if defined(HAS_GLES) + +CGUITextureGLES::CGUITextureGLES(float posX, float posY, float width, float height, const CTextureInfo &texture) +: CGUITextureBase(posX, posY, width, height, texture) +{ + // TODO: OpenGLES +} + +void CGUITextureGLES::Begin() +{ + // TODO: OpenGLES +} + +void CGUITextureGLES::End() +{ + // TODO: OpenGLES +} + +void CGUITextureGLES::Draw(float *x, float *y, float *z, const CRect &texture, const CRect &diffuse, DWORD color, int orientation) +{ + // TODO: OpenGLES +} + +void CGUITextureGLES::DrawQuad(const CRect &rect, DWORD color) +{ + // TODO: OpenGLES +} + +#endif diff --git a/guilib/GUITextureGLES.h b/guilib/GUITextureGLES.h new file mode 100644 index 0000000000..cbfce6ee4c --- /dev/null +++ b/guilib/GUITextureGLES.h @@ -0,0 +1,45 @@ +/*! +\file GUITextureGLES.h +\brief +*/ + +#ifndef GUILIB_GUITEXTUREGLES_H +#define GUILIB_GUITEXTUREGLES_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUITexture.h" + +class CGUITextureGLES : public CGUITextureBase +{ +public: + CGUITextureGLES(float posX, float posY, float width, float height, const CTextureInfo& texture); + static void DrawQuad(const CRect &rect, DWORD color); +protected: + void Begin(); + void Draw(float *x, float *y, float *z, const CRect &texture, const CRect &diffuse, DWORD color, int orientation); + void End(); +}; + +#endif diff --git a/guilib/GUITextureSDL.cpp b/guilib/GUITextureSDL.cpp new file mode 100644 index 0000000000..cacd66d865 --- /dev/null +++ b/guilib/GUITextureSDL.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" +#include "GUITextureSDL.h" +#include "GraphicContext.h" + +using namespace std; + +#ifdef HAS_SDL_2D + +CGUITextureSDL::CGUITextureSDL(float posX, float posY, float width, float height, const CTextureInfo &texture) +: CGUITextureBase(posX, posY, width, height, texture) +{ +} + +void CGUITextureSDL::Allocate() +{ + // allocate our cached textures + for (unsigned int i = 0; i < m_texture.size(); i++) + m_cachedTextures.push_back(CCachedTexture()); +} + +void CGUITextureSDL::Free() +{ + // free our cached textures + for( unsigned int i=0;i<m_cachedTextures.size();++i ) + { + if (m_cachedTextures[i].surface) + SDL_FreeSurface(m_cachedTextures[i].surface); + } + m_cachedTextures.clear(); +} + +void CGUITextureSDL::Draw(float *x, float *y, float *z, const CRect &texture, const CRect &diffuse, color_t color, int orientation) +{ + SDL_Surface* surface = m_texture.m_textures[m_currentFrame]; + float u[2] = { texture.x1, texture.x2 }; + float v[2] = { texture.y1, texture.y2 }; + + // cache texture based on: + // 1. Bounding box + // 2. Diffuse color + int b[4]; + CalcBoundingBox(x, y, 4, b); + CCachedTexture &cached = m_cachedTextures[m_currentFrame]; + if (!cached.surface || cached.width != b[2] || cached.height != b[3] || color != cached.diffuseColor) + { // need to re-render the surface + RenderWithEffects(surface, x, y, u, v, &color, m_diffuse.size() ? m_diffuse.m_textures[0] : NULL, m_diffuseScaleU, m_diffuseScaleV, cached); + } + if (cached.surface) + { + SDL_Rect dst = { (Sint16)b[0], (Sint16)b[1], 0, 0 }; + g_graphicsContext.BlitToScreen(cached.surface, NULL, &dst); + } +} + +void CGUITextureSDL::CalcBoundingBox(float *x, float *y, int n, int *b) +{ + b[0] = (int)x[0], b[2] = 1; + b[1] = (int)y[0], b[3] = 1; + for (int i = 1; i < 4; ++i) + { + if (x[i] < b[0]) b[0] = (int)x[i]; + if (y[i] < b[1]) b[1] = (int)y[i]; + if (x[i]+1 > b[0] + b[2]) b[2] = (int)x[i]+1 - b[0]; + if (y[i]+1 > b[1] + b[3]) b[3] = (int)y[i]+1 - b[1]; + } +} + +#define CLAMP(a,b,c) (a < b) ? b : ((a > c) ? c : a) + +void CGUITextureSDL::GetTexel(float tu, float tv, SDL_Surface *src, BYTE *texel) +{ + int pu1 = (int)floor(tu); + int pv1 = (int)floor(tv); + int pu2 = pu1+1, pv2 = pv1+1; + float du = tu - pu1; + float dv = tv - pv1; + pu1 = CLAMP(pu1,0,src->w-1); + pu2 = CLAMP(pu2,0,src->w-1); + pv1 = CLAMP(pv1,0,src->h-1); + pv2 = CLAMP(pv2,0,src->h-1); + BYTE *tex1 = (BYTE *)src->pixels + pv1 * src->pitch + pu1 * 4; + BYTE *tex2 = (BYTE *)src->pixels + pv1 * src->pitch + pu2 * 4; + BYTE *tex3 = (BYTE *)src->pixels + pv2 * src->pitch + pu1 * 4; + BYTE *tex4 = (BYTE *)src->pixels + pv2 * src->pitch + pu2 * 4; + // average these bytes + texel[0] = (BYTE)((*tex1++ * (1-du) + *tex2++ * du)*(1-dv) + (*tex3++ * (1-du) + *tex4++ * du) * dv); + texel[1] = (BYTE)((*tex1++ * (1-du) + *tex2++ * du)*(1-dv) + (*tex3++ * (1-du) + *tex4++ * du) * dv); + texel[2] = (BYTE)((*tex1++ * (1-du) + *tex2++ * du)*(1-dv) + (*tex3++ * (1-du) + *tex4++ * du) * dv); + texel[3] = (BYTE)((*tex1++ * (1-du) + *tex2++ * du)*(1-dv) + (*tex3++ * (1-du) + *tex4++ * du) * dv); +} + +#define MODULATE(a,b) (b ? ((int)a*(b+1)) >> 8 : 0) +#define ALPHA_BLEND(a,b,c) (c ? ((int)a*(c+1) + b*(255-c)) >> 8 : b) + +void CGUITextureSDL::RenderWithEffects(SDL_Surface *src, float *x, float *y, float *u, float *v, color_t *c, SDL_Surface *diffuse, float diffuseScaleU, float diffuseScaleV, CCachedTexture &dst) +{ + // renders the surface from u[0],v[0] -> u[1],v[1] into the parallelogram defined by x[0],y[0]...x[3],y[3] + // we create a new surface of the appropriate size, and render into it the resized and rotated texture, + // with diffuse modulation etc. as necessary. + + // first create our surface + if (dst.surface) + SDL_FreeSurface(dst.surface); + // calculate the bounding box + int b[4]; + CalcBoundingBox(x, y, 4, b); + dst.width = b[2]; dst.height = b[3]; + dst.diffuseColor = c[0]; + + // create a new texture this size + dst.surface = SDL_CreateRGBSurface(SDL_HWSURFACE, dst.width+1, dst.height+1, 32, RMASK, GMASK, BMASK, AMASK); + if (!dst.surface) + return; // can't create surface + + // vectors of parallelogram in screenspace + float ax = x[1] - x[0]; + float ay = y[1] - y[0]; + float bx = x[3] - x[0]; + float by = y[3] - y[0]; + + // as we're mapping to a parallelogram, don't assume that the vectors are orthogonal (allows for skewed textures later, and no more computation) + // we want the coordinate vector wrt the basis vectors [ax,ay] and [bx,by], so invert the matrix [ax bx;ay by] + u[0] *= src->w; + v[0] *= src->h; + u[1] *= src->w; + v[1] *= src->h; + + float det_m = ax * by - ay * bx; + float m[2][2]; + m[0][0] = by / det_m * (u[1] - u[0]); + m[0][1] = -bx / det_m * (u[1] - u[0]); + m[1][0] = -ay / det_m * (v[1] - v[0]); + m[1][1] = ax / det_m * (v[1] - v[0]); + + // we render from these values in our texture. Note that u[0] may be bigger than u[1] when flipping + const float minU = min(u[0], u[1]) - 0.5f; + const float maxU = max(u[0], u[1]) + 0.5f; + const float minV = min(v[0], v[1]) - 0.5f; + const float maxV = max(v[0], v[1]) + 0.5f; + + SDL_LockSurface(dst.surface); + SDL_LockSurface(src); + if (diffuse) + { + SDL_LockSurface(diffuse); + diffuseScaleU *= diffuse->w / src->w; + diffuseScaleV *= diffuse->h / src->h; + } + + // for speed, find the bounding box of our x, y + // for every pixel in the bounding box, find the corresponding texel + for (int sy = 0; sy <= dst.height; ++sy) + { + for (int sx = 0; sx <= dst.width; ++sx) + { + // find where this pixel corresponds in our texture + float tu = m[0][0] * (sx + b[0] - x[0] - 0.5f) + m[0][1] * (sy + b[1] - y[0] - 0.5f) + u[0]; + float tv = m[1][0] * (sx + b[0] - x[0] - 0.5f) + m[1][1] * (sy + b[1] - y[0] - 0.5f) + v[0]; + if (tu > minU && tu < maxU && tv > minV && tv < maxV) + { // in the texture - render it to screen + BYTE tex[4]; + GetTexel(tu, tv, src, tex); + if (diffuse) + { + BYTE diff[4]; + GetTexel(tu * diffuseScaleU, tv * diffuseScaleV, diffuse, diff); + tex[0] = MODULATE(tex[0], diff[0]); + tex[1] = MODULATE(tex[1], diff[1]); + tex[2] = MODULATE(tex[2], diff[2]); + tex[3] = MODULATE(tex[3], diff[3]); + } + // currently we just use a single color + BYTE *diffuse = (BYTE *)c; + BYTE *screen = (BYTE *)dst.surface->pixels + sy * dst.surface->pitch + sx*4; + screen[0] = MODULATE(tex[0], diffuse[0]); + screen[1] = MODULATE(tex[1], diffuse[1]); + screen[2] = MODULATE(tex[2], diffuse[2]); + screen[3] = MODULATE(tex[3], diffuse[3]); + } + } + } + SDL_UnlockSurface(src); + SDL_UnlockSurface(dst.surface); + if (diffuse) + SDL_UnlockSurface(diffuse); +} + +#endif diff --git a/guilib/GUITextureSDL.h b/guilib/GUITextureSDL.h new file mode 100644 index 0000000000..13d17d3eb3 --- /dev/null +++ b/guilib/GUITextureSDL.h @@ -0,0 +1,67 @@ +/*! +\file GUITextureSDL.h +\brief +*/ + +#ifndef GUILIB_GUITEXTURESDL_H +#define GUILIB_GUITEXTURESDL_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUITexture.h" + +#ifdef HAS_SDL_2D + +class CCachedTexture +{ + public: + CCachedTexture() { surface = NULL; width = height = 0; diffuseColor = 0xffffffff; }; + + SDL_Surface *surface; + int width; + int height; + color_t diffuseColor; +}; + + +class CGUITextureSDL : public CGUITextureBase +{ +public: + CGUITextureSDL(float posX, float posY, float width, float height, const CTextureInfo& texture); + static void DrawQuad(const CRect &rect, color_t color) {}; +protected: + void Draw(float *x, float *y, float *z, const CRect &texture, const CRect &diffuse, color_t color, int orientation); + virtual void Allocate(); + virtual void Free(); + + void CalcBoundingBox(float *x, float *y, int n, int *b); + void GetTexel(float u, float v, SDL_Surface *src, BYTE *texel); + void RenderWithEffects(SDL_Surface *src, float *x, float *y, float *u, float *v, color_t *c, SDL_Surface *diffuse, float diffuseScaleU, float diffuseScaleV, CCachedTexture &dst); + + std::vector <CCachedTexture> m_cachedTextures; +}; + +#endif + +#endif diff --git a/guilib/GUIToggleButtonControl.cpp b/guilib/GUIToggleButtonControl.cpp new file mode 100644 index 0000000000..0cb79b4ad2 --- /dev/null +++ b/guilib/GUIToggleButtonControl.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIToggleButtonControl.h" +#include "GUIWindowManager.h" +#include "GUIDialog.h" +#include "utils/CharsetConverter.h" +#include "utils/GUIInfoManager.h" + +using namespace std; + +CGUIToggleButtonControl::CGUIToggleButtonControl(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& textureFocus, const CTextureInfo& textureNoFocus, const CTextureInfo& altTextureFocus, const CTextureInfo& altTextureNoFocus, const CLabelInfo &labelInfo) + : CGUIButtonControl(parentID, controlID, posX, posY, width, height, textureFocus, textureNoFocus, labelInfo) + , m_selectButton(parentID, controlID, posX, posY, width, height, altTextureFocus, altTextureNoFocus, labelInfo) +{ + m_toggleSelect = 0; + ControlType = GUICONTROL_TOGGLEBUTTON; +} + +CGUIToggleButtonControl::~CGUIToggleButtonControl(void) +{ +} + +void CGUIToggleButtonControl::Render() +{ + // ask our infoManager whether we are selected or not... + if (m_toggleSelect) + m_bSelected = g_infoManager.GetBool(m_toggleSelect, m_parentID); + + if (m_bSelected) + { + // render our Alternate textures... + m_selectButton.SetFocus(HasFocus()); + m_selectButton.SetVisible(IsVisible()); + m_selectButton.SetEnabled(!IsDisabled()); + m_selectButton.SetPulseOnSelect(m_pulseOnSelect); + m_selectButton.Render(); + CGUIControl::Render(); + } + else + { // render our Normal textures... + CGUIButtonControl::Render(); + } +} + +bool CGUIToggleButtonControl::OnAction(const CAction &action) +{ + if (action.id == ACTION_SELECT_ITEM) + { + m_bSelected = !m_bSelected; + } + return CGUIButtonControl::OnAction(action); +} + +void CGUIToggleButtonControl::AllocResources() +{ + CGUIButtonControl::AllocResources(); + m_selectButton.AllocResources(); +} + +void CGUIToggleButtonControl::FreeResources() +{ + CGUIButtonControl::FreeResources(); + m_selectButton.FreeResources(); +} + +void CGUIToggleButtonControl::DynamicResourceAlloc(bool bOnOff) +{ + CGUIButtonControl::DynamicResourceAlloc(bOnOff); + m_selectButton.DynamicResourceAlloc(bOnOff); +} + +void CGUIToggleButtonControl::SetInvalid() +{ + CGUIButtonControl::SetInvalid(); + m_selectButton.SetInvalid(); +} + +void CGUIToggleButtonControl::SetPosition(float posX, float posY) +{ + CGUIButtonControl::SetPosition(posX, posY); + m_selectButton.SetPosition(posX, posY); +} + +void CGUIToggleButtonControl::SetWidth(float width) +{ + CGUIButtonControl::SetWidth(width); + m_selectButton.SetWidth(width); +} + +void CGUIToggleButtonControl::SetHeight(float height) +{ + CGUIButtonControl::SetHeight(height); + m_selectButton.SetHeight(height); +} + +void CGUIToggleButtonControl::UpdateColors() +{ + CGUIButtonControl::UpdateColors(); + m_selectButton.UpdateColors(); +} + +void CGUIToggleButtonControl::SetLabel(const string &strLabel) +{ + CGUIButtonControl::SetLabel(strLabel); + m_selectButton.SetLabel(strLabel); +} + +void CGUIToggleButtonControl::SetAltLabel(const string &label) +{ + if (label.size()) + m_selectButton.SetLabel(label); +} + +CStdString CGUIToggleButtonControl::GetLabel() const +{ + if (m_bSelected) + return m_selectButton.GetLabel(); + return CGUIButtonControl::GetLabel(); +} + +void CGUIToggleButtonControl::SetAltClickActions(const vector<CGUIActionDescriptor> &clickActions) +{ + m_selectButton.SetClickActions(clickActions); +} + +void CGUIToggleButtonControl::OnClick() +{ + // the ! is here as m_bSelected gets updated before this is called + if (!m_bSelected && m_selectButton.HasClickActions()) + m_selectButton.OnClick(); + else + CGUIButtonControl::OnClick(); +} diff --git a/guilib/GUIToggleButtonControl.h b/guilib/GUIToggleButtonControl.h new file mode 100644 index 0000000000..177ddd057f --- /dev/null +++ b/guilib/GUIToggleButtonControl.h @@ -0,0 +1,66 @@ +/*! +\file GUIToggleButtonControl.h +\brief +*/ + +#ifndef GUILIB_GUITOGGLEBUTTONCONTROL_H +#define GUILIB_GUITOGGLEBUTTONCONTROL_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIButtonControl.h" + +/*! + \ingroup controls + \brief + */ +class CGUIToggleButtonControl : public CGUIButtonControl +{ +public: + CGUIToggleButtonControl(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& textureFocus, const CTextureInfo& textureNoFocus, const CTextureInfo& altTextureFocus, const CTextureInfo& altTextureNoFocus, const CLabelInfo &labelInfo); + virtual ~CGUIToggleButtonControl(void); + virtual CGUIToggleButtonControl *Clone() const { return new CGUIToggleButtonControl(*this); }; + + virtual void Render(); + virtual bool OnAction(const CAction &action); + virtual void AllocResources(); + virtual void FreeResources(); + virtual void DynamicResourceAlloc(bool bOnOff); + virtual void SetPosition(float posX, float posY); + virtual void SetWidth(float width); + virtual void SetHeight(float height); + void SetLabel(const std::string& strLabel); + void SetAltLabel(const std::string& label); + virtual CStdString GetLabel() const; + void SetToggleSelect(int toggleSelect) { m_toggleSelect = toggleSelect; }; + void SetAltClickActions(const std::vector<CGUIActionDescriptor> &clickActions); + +protected: + virtual void UpdateColors(); + virtual void OnClick(); + virtual void SetInvalid(); + CGUIButtonControl m_selectButton; + int m_toggleSelect; +}; +#endif diff --git a/guilib/GUIVideoControl.cpp b/guilib/GUIVideoControl.cpp new file mode 100644 index 0000000000..3a0f9cccac --- /dev/null +++ b/guilib/GUIVideoControl.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" +#include "GUIVideoControl.h" +#include "GUIWindowManager.h" +#include "MouseStat.h" +#include "Application.h" +#ifdef HAS_VIDEO_PLAYBACK +#include "cores/VideoRenderers/RenderManager.h" +#else +#include "cores/DummyVideoPlayer.h" +#endif + +CGUIVideoControl::CGUIVideoControl(int parentID, int controlID, float posX, float posY, float width, float height) + : CGUIControl(parentID, controlID, posX, posY, width, height) +{ + ControlType = GUICONTROL_VIDEO; +} + +CGUIVideoControl::~CGUIVideoControl(void) +{} + + +void CGUIVideoControl::Render() +{ +#ifdef HAS_VIDEO_PLAYBACK + // don't render if we aren't playing video, or if the renderer isn't started + // (otherwise the lock we have from CApplication::Render() may clash with the startup + // locks in the RenderManager.) + if (g_application.IsPlayingVideo() && g_renderManager.IsStarted()) + { +#else + if (g_application.IsPlayingVideo()) + { +#endif + if (!g_application.m_pPlayer->IsPaused()) + g_application.ResetScreenSaver(); + + g_graphicsContext.SetViewWindow(m_posX, m_posY, m_posX + m_width, m_posY + m_height); + +#ifdef HAS_VIDEO_PLAYBACK + color_t alpha = g_graphicsContext.MergeAlpha(0xFF000000) >> 24; + g_renderManager.RenderUpdate(false, 0, alpha); +#else + ((CDummyVideoPlayer *)g_application.m_pPlayer)->Render(); +#endif + } + CGUIControl::Render(); +} + +bool CGUIVideoControl::OnMouseClick(int button, const CPoint &point) +{ // mouse has clicked in the video control + // switch to fullscreen video + if (!g_application.IsPlayingVideo()) return false; + if (button == MOUSE_LEFT_BUTTON) + { + CGUIMessage message(GUI_MSG_FULLSCREEN, GetID(), GetParentID()); + g_graphicsContext.SendMessage(message); + return true; + } + if (button == MOUSE_RIGHT_BUTTON) + { // toggle the playlist window + if (m_gWindowManager.GetActiveWindow() == WINDOW_VIDEO_PLAYLIST) + m_gWindowManager.PreviousWindow(); + else + m_gWindowManager.ActivateWindow(WINDOW_VIDEO_PLAYLIST); + // reset the mouse button. + g_Mouse.bClick[MOUSE_RIGHT_BUTTON] = false; + return true; + } + return false; +} + +bool CGUIVideoControl::OnMouseOver(const CPoint &point) +{ + // unfocusable, so return true + CGUIControl::OnMouseOver(point); + return false; +} + +bool CGUIVideoControl::CanFocus() const +{ // unfocusable + return false; +} + +bool CGUIVideoControl::CanFocusFromPoint(const CPoint &point, CGUIControl **control, CPoint &controlPoint) const +{ // mouse is allowed to focus this control, but it doesn't actually receive focus + controlPoint = point; + m_transform.InverseTransformPosition(controlPoint.x, controlPoint.y); + if (HitTest(controlPoint)) + { + *control = (CGUIControl *)this; + return true; + } + *control = NULL; + return false; +} diff --git a/guilib/GUIVideoControl.h b/guilib/GUIVideoControl.h new file mode 100644 index 0000000000..ef82527b66 --- /dev/null +++ b/guilib/GUIVideoControl.h @@ -0,0 +1,52 @@ +/*! +\file GUIVideoControl.h +\brief +*/ + +#ifndef GUILIB_GUIVIDEOCONTROL_H +#define GUILIB_GUIVIDEOCONTROL_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControl.h" + +/*! + \ingroup controls + \brief + */ +class CGUIVideoControl : + public CGUIControl +{ +public: + CGUIVideoControl(int parentID, int controlID, float posX, float posY, float width, float height); + virtual ~CGUIVideoControl(void); + virtual CGUIVideoControl *Clone() const { return new CGUIVideoControl(*this); }; + + virtual void Render(); + virtual bool OnMouseClick(int button, const CPoint &point); + virtual bool OnMouseOver(const CPoint &point); + virtual bool CanFocus() const; + virtual bool CanFocusFromPoint(const CPoint &point, CGUIControl **control, CPoint &controlPoint) const; +}; +#endif diff --git a/guilib/GUIVisualisationControl.cpp b/guilib/GUIVisualisationControl.cpp new file mode 100644 index 0000000000..acd7554147 --- /dev/null +++ b/guilib/GUIVisualisationControl.cpp @@ -0,0 +1,484 @@ +#include "GUIVisualisationControl.h" +#include "GUIUserMessages.h" +#include "Application.h" +#include "visualizations/Visualisation.h" +#include "visualizations/VisualisationFactory.h" +#include "visualizations/fft.h" +#include "Util.h" +#include "utils/CriticalSection.h" +#include "utils/log.h" +#include "utils/SingleLock.h" +#include "utils/GUIInfoManager.h" +#include "GUISettings.h" + +using namespace std; +using namespace MUSIC_INFO; + +#define LABEL_ROW1 10 +#define LABEL_ROW2 11 +#define LABEL_ROW3 12 + +// uggly hack, we can only allow one visualisation at one time or stuff starts crashing +static CCriticalSection m_critSection; +static bool m_globalvis = false; + +CAudioBuffer::CAudioBuffer(int iSize) +{ + m_iLen = iSize; + m_pBuffer = new short[iSize]; +} + +CAudioBuffer::~CAudioBuffer() +{ + delete [] m_pBuffer; +} + +const short* CAudioBuffer::Get() const +{ + return m_pBuffer; +} + +void CAudioBuffer::Set(const unsigned char* psBuffer, int iSize, int iBitsPerSample) +{ + if (iSize<0) + { + return; + } + + if (iBitsPerSample == 16) + { + iSize /= 2; + for (int i = 0; i < iSize && i < m_iLen; i++) + { // 16 bit -> convert to short directly + m_pBuffer[i] = ((short *)psBuffer)[i]; + } + } + else if (iBitsPerSample == 8) + { + for (int i = 0; i < iSize && i < m_iLen; i++) + { // 8 bit -> convert to signed short by multiplying by 256 + m_pBuffer[i] = ((short)((char *)psBuffer)[i]) << 8; + } + } + else // assume 24 bit data + { + iSize /= 3; + for (int i = 0; i < iSize && i < m_iLen; i++) + { // 24 bit -> ignore least significant byte and convert to signed short + m_pBuffer[i] = (((int)psBuffer[3 * i + 1]) << 0) + (((int)((char *)psBuffer)[3 * i + 2]) << 8); + } + } + for (int i = iSize; i < m_iLen;++i) m_pBuffer[i] = 0; +} + +CGUIVisualisationControl::CGUIVisualisationControl(int parentID, int controlID, float posX, float posY, float width, float height) + : CGUIControl(parentID, controlID, posX, posY, width, height) +{ + m_pVisualisation = NULL; + m_bInitialized = false; + m_iNumBuffers = 0; + m_currentVis = ""; + ControlType = GUICONTROL_VISUALISATION; +} + +CGUIVisualisationControl::CGUIVisualisationControl(const CGUIVisualisationControl &from) +: CGUIControl(from) +{ + m_pVisualisation = NULL; + m_bInitialized = false; + m_iNumBuffers = 0; + m_currentVis = ""; + ControlType = GUICONTROL_VISUALISATION; +} + +CGUIVisualisationControl::~CGUIVisualisationControl(void) +{ +} + +void CGUIVisualisationControl::FreeVisualisation() +{ + if (!m_bInitialized) return; + m_bInitialized = false; + // tell our app that we're going + CGUIMessage msg(GUI_MSG_VISUALISATION_UNLOADING, 0, 0); + g_graphicsContext.SendMessage(msg); + + CSingleLock lock (m_critSection); + + CLog::Log(LOGDEBUG, "FreeVisualisation() started"); + if (g_application.m_pPlayer) + g_application.m_pPlayer->UnRegisterAudioCallback(); + if (m_pVisualisation) + { + OutputDebugString("Visualisation::Stop()\n"); + g_graphicsContext.CaptureStateBlock(); + m_pVisualisation->Stop(); + g_graphicsContext.ApplyStateBlock(); + + OutputDebugString("delete Visualisation()\n"); + delete m_pVisualisation; + + /* we released the global vis spot */ + m_globalvis = false; + } + m_pVisualisation = NULL; + ClearBuffers(); + CLog::Log(LOGDEBUG, "FreeVisualisation() done"); +} + +void CGUIVisualisationControl::LoadVisualisation() +{ + CSingleLock lock (m_critSection); + if (m_pVisualisation) + FreeVisualisation(); + + m_bInitialized = false; + + /* check if any other control beat us to the punch */ + if(m_globalvis) + return; + + CVisualisationFactory factory; + CStdString strVisz, strModule; + m_currentVis = g_guiSettings.GetString("mymusic.visualisation"); + + if (m_currentVis.Equals("None")) + return; + + // check if it's a multi-vis and if it is , get it's module name + { + int colonPos = m_currentVis.ReverseFind(":"); + if ( colonPos > 0 ) + { + strModule = m_currentVis.Mid( colonPos+1 ); + strVisz = m_currentVis.Mid( 0, colonPos ); + m_pVisualisation = factory.LoadVisualisation(strVisz, strModule); + } + else + { + strVisz = m_currentVis; + m_pVisualisation = factory.LoadVisualisation(strVisz); + } + } + if (m_pVisualisation) + { + g_graphicsContext.CaptureStateBlock(); + float x = g_graphicsContext.ScaleFinalXCoord(GetXPosition(), GetYPosition()); + float y = g_graphicsContext.ScaleFinalYCoord(GetXPosition(), GetYPosition()); + float w = g_graphicsContext.ScaleFinalXCoord(GetXPosition() + GetWidth(), GetYPosition() + GetHeight()) - x; + float h = g_graphicsContext.ScaleFinalYCoord(GetXPosition() + GetWidth(), GetYPosition() + GetHeight()) - y; + if (x < 0) x = 0; + if (y < 0) y = 0; + if (x + w > g_graphicsContext.GetWidth()) w = g_graphicsContext.GetWidth() - x; + if (y + h > g_graphicsContext.GetHeight()) h = g_graphicsContext.GetHeight() - y; + + m_pVisualisation->Create((int)(x+0.5f), (int)(y+0.5f), (int)(w+0.5f), (int)(h+0.5f)); + g_graphicsContext.ApplyStateBlock(); + VerifyGLState(); + if (g_application.m_pPlayer) + g_application.m_pPlayer->RegisterAudioCallback(this); + + // Create new audio buffers + CreateBuffers(); + + m_globalvis = true; + } + + // tell our app that we're back + CGUIMessage msg(GUI_MSG_VISUALISATION_LOADED, 0, 0, 0, 0, m_pVisualisation); + g_graphicsContext.SendMessage(msg); +} + +void CGUIVisualisationControl::UpdateVisibility(const CGUIListItem *item) +{ + CGUIControl::UpdateVisibility(item); + if (!IsVisible() && m_bInitialized) + FreeVisualisation(); +} + +void CGUIVisualisationControl::Render() +{ + if (m_pVisualisation == NULL) + { // check if we need to load + if (g_application.IsPlayingAudio()) + { + LoadVisualisation(); + } + CGUIControl::Render(); + + return; + } + else + { // check if we need to unload + if (!g_application.IsPlayingAudio()) + { // deinit + FreeVisualisation(); + CGUIControl::Render(); + return; + } + else if (!m_currentVis.Equals(g_guiSettings.GetString("mymusic.visualisation"))) + { // vis changed - reload + LoadVisualisation(); + + CGUIControl::Render(); + + return; + } + } + CSingleLock lock (m_critSection); + if (m_pVisualisation) + { + if (m_bInitialized) + { + // set the viewport - note: We currently don't have any control over how + // the visualisation renders, so the best we can do is attempt to define + // a viewport?? + g_graphicsContext.SetViewPort(m_posX, m_posY, m_width, m_height); + try + { + g_graphicsContext.CaptureStateBlock(); + m_pVisualisation->Render(); + g_graphicsContext.ApplyStateBlock(); + } + catch (...) + { + CLog::Log(LOGERROR, "Exception in Visualisation::Render()"); + } + // clear the viewport + g_graphicsContext.RestoreViewPort(); + } + } + + CGUIControl::Render(); +} + + +void CGUIVisualisationControl::OnInitialize(int iChannels, int iSamplesPerSec, int iBitsPerSample) +{ + CSingleLock lock (m_critSection); + if (!m_pVisualisation) + return ; + CLog::Log(LOGDEBUG, "OnInitialize() started"); + + m_iChannels = iChannels; + m_iSamplesPerSec = iSamplesPerSec; + m_iBitsPerSample = iBitsPerSample; + + // Start the visualisation (this loads settings etc.) + CStdString strFile = CUtil::GetFileName(g_application.CurrentFile()); + OutputDebugString("Visualisation::Start()\n"); + m_pVisualisation->Start(m_iChannels, m_iSamplesPerSec, m_iBitsPerSample, strFile); + if (!m_bInitialized) + { + UpdateTrack(); + } + m_bInitialized = true; + CLog::Log(LOGDEBUG, "OnInitialize() done"); +} + +void CGUIVisualisationControl::OnAudioData(const unsigned char* pAudioData, int iAudioDataLength) +{ + CSingleLock lock (m_critSection); + if (!m_pVisualisation) + return ; + if (!m_bInitialized) return ; + + // FIXME: iAudioDataLength should never be less than 0 + if (iAudioDataLength<0) + return; + + // Save our audio data in the buffers + auto_ptr<CAudioBuffer> pBuffer ( new CAudioBuffer(2*AUDIO_BUFFER_SIZE) ); + pBuffer->Set(pAudioData, iAudioDataLength, m_iBitsPerSample); + m_vecBuffers.push_back( pBuffer.release() ); + + if ( (int)m_vecBuffers.size() < m_iNumBuffers) return ; + + auto_ptr<CAudioBuffer> ptrAudioBuffer ( m_vecBuffers.front() ); + m_vecBuffers.pop_front(); + // Fourier transform the data if the vis wants it... + if (m_bWantsFreq) + { + // Convert to floats + const short* psAudioData = ptrAudioBuffer->Get(); + for (int i = 0; i < 2*AUDIO_BUFFER_SIZE; i++) + { + m_fFreq[i] = (float)psAudioData[i]; + } + + // FFT the data + twochanwithwindow(m_fFreq, AUDIO_BUFFER_SIZE); + + // Normalize the data + float fMinData = (float)AUDIO_BUFFER_SIZE * AUDIO_BUFFER_SIZE * 3 / 8 * 0.5 * 0.5; // 3/8 for the Hann window, 0.5 as minimum amplitude + float fInvMinData = 1.0f/fMinData; + for (int i = 0; i < AUDIO_BUFFER_SIZE + 2; i++) + { + m_fFreq[i] *= fInvMinData; + } + + // Transfer data to our visualisation + try + { + m_pVisualisation->AudioData(ptrAudioBuffer->Get(), AUDIO_BUFFER_SIZE, m_fFreq, AUDIO_BUFFER_SIZE); + } + catch (...) + { + CLog::Log(LOGERROR, "Exception in Visualisation::AudioData()"); + } + } + else + { // Transfer data to our visualisation + try + { + m_pVisualisation->AudioData(ptrAudioBuffer->Get(), AUDIO_BUFFER_SIZE, NULL, 0); + } + catch (...) + { + CLog::Log(LOGERROR, "Exception in Visualisation::AudioData()"); + } + } + return ; +} + +bool CGUIVisualisationControl::OnAction(const CAction &action) +{ + if (!m_pVisualisation) return false; + enum CVisualisation::VIS_ACTION visAction = CVisualisation::VIS_ACTION_NONE; + if (action.id == ACTION_VIS_PRESET_NEXT) + visAction = CVisualisation::VIS_ACTION_NEXT_PRESET; + else if (action.id == ACTION_VIS_PRESET_PREV) + visAction = CVisualisation::VIS_ACTION_PREV_PRESET; + else if (action.id == ACTION_VIS_PRESET_LOCK) + visAction = CVisualisation::VIS_ACTION_LOCK_PRESET; + else if (action.id == ACTION_VIS_PRESET_RANDOM) + visAction = CVisualisation::VIS_ACTION_RANDOM_PRESET; + else if (action.id == ACTION_VIS_RATE_PRESET_PLUS) + visAction = CVisualisation::VIS_ACTION_RATE_PRESET_PLUS; + else if (action.id == ACTION_VIS_RATE_PRESET_MINUS) + visAction = CVisualisation::VIS_ACTION_RATE_PRESET_MINUS; + + return m_pVisualisation->OnAction(visAction); +} + +bool CGUIVisualisationControl::UpdateTrack() +{ + bool handled = false; + + if ( m_pVisualisation ) + { + // get the current album art filename + m_AlbumThumb = g_infoManager.GetImage(MUSICPLAYER_COVER, WINDOW_INVALID); + + // get the current track tag + const CMusicInfoTag* tag = g_infoManager.GetCurrentSongTag(); + + if (m_AlbumThumb == "DefaultAlbumCover.png") + m_AlbumThumb = ""; + else + CLog::Log(LOGDEBUG,"Updating visualisation albumart: %s", m_AlbumThumb.c_str()); + + // inform the visulisation of the current album art + if ( m_pVisualisation->OnAction( CVisualisation::VIS_ACTION_UPDATE_ALBUMART, + (void*)( m_AlbumThumb.c_str() ) ) ) + handled = true; + + // inform the visualisation of the current track's tag information + if ( tag && m_pVisualisation->OnAction( CVisualisation::VIS_ACTION_UPDATE_TRACK, + (void*)tag ) ) + handled = true; + } + return handled; +} + +bool CGUIVisualisationControl::OnMessage(CGUIMessage &message) +{ + if (message.GetMessage() == GUI_MSG_GET_VISUALISATION) + { + message.SetPointer(GetVisualisation()); + return true; + } + else if (message.GetMessage() == GUI_MSG_VISUALISATION_ACTION) + { + CAction action; + action.id = message.GetParam1(); + return OnAction(action); + } + else if (message.GetMessage() == GUI_MSG_PLAYBACK_STARTED) + { + if (IsVisible() && UpdateTrack()) return true; + } + return CGUIControl::OnMessage(message); +} + +void CGUIVisualisationControl::CreateBuffers() +{ + CSingleLock lock (m_critSection); + ClearBuffers(); + + // Get the number of buffers from the current vis + VIS_INFO info; + m_pVisualisation->GetInfo(&info); + m_iNumBuffers = info.iSyncDelay + 1; + m_bWantsFreq = info.bWantsFreq; + if (m_iNumBuffers > MAX_AUDIO_BUFFERS) + m_iNumBuffers = MAX_AUDIO_BUFFERS; + if (m_iNumBuffers < 1) + m_iNumBuffers = 1; +} + + +void CGUIVisualisationControl::ClearBuffers() +{ + CSingleLock lock (m_critSection); + m_bWantsFreq = false; + m_iNumBuffers = 0; + + while (m_vecBuffers.size() > 0) + { + CAudioBuffer* pAudioBuffer = m_vecBuffers.front(); + delete pAudioBuffer; + m_vecBuffers.pop_front(); + } + for (int j = 0; j < AUDIO_BUFFER_SIZE*2; j++) + { + m_fFreq[j] = 0.0f; + } +} + +void CGUIVisualisationControl::FreeResources() +{ + FreeVisualisation(); + CGUIControl::FreeResources(); +} + +CVisualisation *CGUIVisualisationControl::GetVisualisation() +{ + CSingleLock lock (m_critSection); + return m_pVisualisation; +} + +bool CGUIVisualisationControl::OnMouseOver(const CPoint &point) +{ + // unfocusable, so return true + CGUIControl::OnMouseOver(point); + return false; +} + +bool CGUIVisualisationControl::CanFocus() const +{ // unfocusable + return false; +} + +bool CGUIVisualisationControl::CanFocusFromPoint(const CPoint &point, CGUIControl **control, CPoint &controlPoint) const +{ // mouse is allowed to focus this control, but it doesn't actually receive focus + controlPoint = point; + m_transform.InverseTransformPosition(controlPoint.x, controlPoint.y); + if (HitTest(controlPoint)) + { + *control = (CGUIControl *)this; + return true; + } + *control = NULL; + return false; +} diff --git a/guilib/GUIVisualisationControl.h b/guilib/GUIVisualisationControl.h new file mode 100644 index 0000000000..1d73337969 --- /dev/null +++ b/guilib/GUIVisualisationControl.h @@ -0,0 +1,88 @@ +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControl.h" +#include "cores/IAudioCallback.h" + +#include <list> + +// forward definitions +class CVisualisation; + +#define AUDIO_BUFFER_SIZE 512 // MUST BE A POWER OF 2!!! +#define MAX_AUDIO_BUFFERS 16 + +class CAudioBuffer +{ +public: + CAudioBuffer(int iSize); + virtual ~CAudioBuffer(); + const short* Get() const; + void Set(const unsigned char* psBuffer, int iSize, int iBitsPerSample); +private: + CAudioBuffer(); + short* m_pBuffer; + int m_iLen; +}; + +class CGUIVisualisationControl : + public CGUIControl, public IAudioCallback +{ +public: + CGUIVisualisationControl(int parentID, int controlID, float posX, float posY, float width, float height); + CGUIVisualisationControl(const CGUIVisualisationControl &from); + virtual ~CGUIVisualisationControl(void); + virtual CGUIVisualisationControl *Clone() const { return new CGUIVisualisationControl(*this); }; + + virtual void Render(); + virtual void UpdateVisibility(const CGUIListItem *item = NULL); + virtual void FreeResources(); + virtual void OnInitialize(int iChannels, int iSamplesPerSec, int iBitsPerSample); + virtual void OnAudioData(const unsigned char* pAudioData, int iAudioDataLength); + virtual bool OnAction(const CAction &action); + virtual bool OnMessage(CGUIMessage& message); + virtual bool OnMouseOver(const CPoint &point); + virtual bool CanFocus() const; + virtual bool CanFocusFromPoint(const CPoint &point, CGUIControl **control, CPoint &controlPoint) const; + + CVisualisation *GetVisualisation(); +private: + void FreeVisualisation(); + void LoadVisualisation(); + void CreateBuffers(); + void ClearBuffers(); + bool UpdateTrack(); + CStdString m_currentVis; + CVisualisation* m_pVisualisation; + + int m_iChannels; + int m_iSamplesPerSec; + int m_iBitsPerSample; + std::list<CAudioBuffer*> m_vecBuffers; + int m_iNumBuffers; // Number of Audio buffers + bool m_bWantsFreq; + float m_fFreq[2*AUDIO_BUFFER_SIZE]; // Frequency data + bool m_bCalculate_Freq; // True if the vis wants freq data + bool m_bInitialized; + CStdString m_AlbumThumb; +}; diff --git a/guilib/GUIWindow.cpp b/guilib/GUIWindow.cpp new file mode 100644 index 0000000000..30c7a90b7f --- /dev/null +++ b/guilib/GUIWindow.cpp @@ -0,0 +1,1022 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" +#include "GUIWindow.h" +#include "GUIWindowManager.h" +#include "Key.h" +#include "LocalizeStrings.h" +#include "Settings.h" +#include "GUIControlFactory.h" +#include "GUIControlGroup.h" +#include "GUIControlProfiler.h" +#ifdef PRE_SKIN_VERSION_9_10_COMPATIBILITY +#include "GUIEditControl.h" +#endif + +#include "SkinInfo.h" +#include "utils/GUIInfoManager.h" +#include "utils/log.h" +#include "utils/SingleLock.h" +#include "ButtonTranslator.h" +#include "XMLUtils.h" +#include "MouseStat.h" + +#ifdef HAS_PERFORMANCE_SAMPLE +#include "utils/PerformanceSample.h" +#endif + +using namespace std; + +CGUIWindow::CGUIWindow(int id, const CStdString &xmlFile) +{ + SetID(id); + m_xmlFile = xmlFile; + m_idRange = 1; + m_lastControlID = 0; + m_bRelativeCoords = false; + m_overlayState = OVERLAY_STATE_PARENT_WINDOW; // Use parent or previous window's state + m_coordsRes = g_guiSettings.m_LookAndFeelResolution; + m_isDialog = false; + m_needsScaling = true; + m_windowLoaded = false; + m_loadOnDemand = true; + m_renderOrder = 0; + m_dynamicResourceAlloc = true; + m_previousWindow = WINDOW_INVALID; + m_animationsEnabled = true; + m_manualRunActions = false; +} + +CGUIWindow::~CGUIWindow(void) +{} + +bool CGUIWindow::Load(const CStdString& strFileName, bool bContainsPath) +{ +#ifdef HAS_PERFORMANCE_SAMPLE + CPerformanceSample aSample("WindowLoad-" + strFileName, true); +#endif + + if (m_windowLoaded) + return true; // no point loading if it's already there + + LARGE_INTEGER start; + QueryPerformanceCounter(&start); + + RESOLUTION resToUse = RES_INVALID; + CLog::Log(LOGINFO, "Loading skin file: %s", strFileName.c_str()); + TiXmlDocument xmlDoc; + // Find appropriate skin folder + resolution to load from + CStdString strPath; + CStdString strLowerPath; + if (bContainsPath) + strPath = strFileName; + else + { + // FIXME: strLowerPath needs to eventually go since resToUse can get incorrectly overridden + strLowerPath = g_SkinInfo.GetSkinPath(CStdString(strFileName).ToLower(), &resToUse); + strPath = g_SkinInfo.GetSkinPath(strFileName, &resToUse); + } + + if (!bContainsPath) + m_coordsRes = resToUse; + + bool ret = LoadXML(strPath.c_str(), strLowerPath.c_str()); + + LARGE_INTEGER end, freq; + QueryPerformanceCounter(&end); + QueryPerformanceFrequency(&freq); + CLog::Log(LOGDEBUG,"Load %s: %.2fms", m_xmlFile.c_str(), 1000.f * (end.QuadPart - start.QuadPart) / freq.QuadPart); + + return ret; +} + +bool CGUIWindow::LoadXML(const CStdString &strPath, const CStdString &strLowerPath) +{ + TiXmlDocument xmlDoc; + if ( !xmlDoc.LoadFile(strPath) && !xmlDoc.LoadFile(CStdString(strPath).ToLower()) && !xmlDoc.LoadFile(strLowerPath)) + { + CLog::Log(LOGERROR, "unable to load:%s, Line %d\n%s", strPath.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc()); + SetID(WINDOW_INVALID); + return false; + } + + return Load(xmlDoc); +} + +bool CGUIWindow::Load(TiXmlDocument &xmlDoc) +{ + TiXmlElement* pRootElement = xmlDoc.RootElement(); + if (strcmpi(pRootElement->Value(), "window")) + { + CLog::Log(LOGERROR, "file : XML file doesnt contain <window>"); + return false; + } + + // set the scaling resolution so that any control creation or initialisation can + // be done with respect to the correct aspect ratio + g_graphicsContext.SetScalingResolution(m_coordsRes, 0, 0, m_needsScaling); + + // Resolve any includes that may be present + g_SkinInfo.ResolveIncludes(pRootElement); + // now load in the skin file + SetDefaults(); + + + CGUIControlFactory::GetMultipleString(pRootElement, "onload", m_loadActions); + CGUIControlFactory::GetMultipleString(pRootElement, "onunload", m_unloadActions); + + TiXmlElement *pChild = pRootElement->FirstChildElement(); + while (pChild) + { + CStdString strValue = pChild->Value(); + if (strValue == "type" && pChild->FirstChild()) + { + // if we have are a window type (ie not a dialog), and we have <type>dialog</type> + // then make this window act like a dialog + if (!IsDialog() && strcmpi(pChild->FirstChild()->Value(), "dialog") == 0) + m_isDialog = true; + } + else if (strValue == "previouswindow" && pChild->FirstChild()) + { + m_previousWindow = CButtonTranslator::TranslateWindowString(pChild->FirstChild()->Value()); + } + else if (strValue == "defaultcontrol" && pChild->FirstChild()) + { + const char *always = pChild->Attribute("always"); + if (always && strcmpi(always, "true") == 0) + m_defaultAlways = true; + m_defaultControl = atoi(pChild->FirstChild()->Value()); + } + else if (strValue == "visible" && pChild->FirstChild()) + { + CGUIControlFactory::GetConditionalVisibility(pRootElement, m_visibleCondition); + } + else if (strValue == "animation" && pChild->FirstChild()) + { + FRECT rect = { 0, 0, (float)g_settings.m_ResInfo[m_coordsRes].iWidth, (float)g_settings.m_ResInfo[m_coordsRes].iHeight }; + CAnimation anim; + anim.Create(pChild, rect); + m_animations.push_back(anim); + } + else if (strValue == "zorder" && pChild->FirstChild()) + { + m_renderOrder = atoi(pChild->FirstChild()->Value()); + } + else if (strValue == "coordinates") + { + // resolve any includes within coordinates tag (such as multiple origin includes) + g_SkinInfo.ResolveIncludes(pChild); + TiXmlNode* pSystem = pChild->FirstChild("system"); + if (pSystem) + { + int iCoordinateSystem = atoi(pSystem->FirstChild()->Value()); + m_bRelativeCoords = (iCoordinateSystem == 1); + } + + CGUIControlFactory::GetFloat(pChild, "posx", m_posX); + CGUIControlFactory::GetFloat(pChild, "posy", m_posY); + + TiXmlElement *originElement = pChild->FirstChildElement("origin"); + while (originElement) + { + COrigin origin; + g_SkinInfo.ResolveConstant(originElement->Attribute("x"), origin.x); + g_SkinInfo.ResolveConstant(originElement->Attribute("y"), origin.y); + if (originElement->FirstChild()) + origin.condition = g_infoManager.TranslateString(originElement->FirstChild()->Value()); + m_origins.push_back(origin); + originElement = originElement->NextSiblingElement("origin"); + } + } + else if (strValue == "camera") + { // z is fixed + g_SkinInfo.ResolveConstant(pChild->Attribute("x"), m_camera.x); + g_SkinInfo.ResolveConstant(pChild->Attribute("y"), m_camera.y); + m_hasCamera = true; + } + else if (strValue == "controls") + { + // resolve any includes within controls tag (such as whole <control> includes) + g_SkinInfo.ResolveIncludes(pChild); + + TiXmlElement *pControl = pChild->FirstChildElement(); + while (pControl) + { + if (strcmpi(pControl->Value(), "control") == 0) + { + LoadControl(pControl, NULL); + } + pControl = pControl->NextSiblingElement(); + } + } + else if (strValue == "allowoverlay") + { + bool overlay = false; + if (XMLUtils::GetBoolean(pRootElement, "allowoverlay", overlay)) + m_overlayState = overlay ? OVERLAY_STATE_SHOWN : OVERLAY_STATE_HIDDEN; + } + + pChild = pChild->NextSiblingElement(); + } + LoadAdditionalTags(pRootElement); + + m_windowLoaded = true; + OnWindowLoaded(); + return true; +} + +void CGUIWindow::LoadControl(TiXmlElement* pControl, CGUIControlGroup *pGroup) +{ + // get control type + CGUIControlFactory factory; + + FRECT rect = { 0, 0, (float)g_settings.m_ResInfo[m_coordsRes].iWidth, (float)g_settings.m_ResInfo[m_coordsRes].iHeight }; + if (pGroup) + { + rect.left = pGroup->GetXPosition(); + rect.top = pGroup->GetYPosition(); + rect.right = rect.left + pGroup->GetWidth(); + rect.bottom = rect.top + pGroup->GetHeight(); + } + CGUIControl* pGUIControl = factory.Create(GetID(), rect, pControl); + if (pGUIControl) + { + float maxX = pGUIControl->GetXPosition() + pGUIControl->GetWidth(); + if (maxX > m_width) + { + m_width = maxX; + } + + float maxY = pGUIControl->GetYPosition() + pGUIControl->GetHeight(); + if (maxY > m_height) + { + m_height = maxY; + } + // if we are in a group, add to the group, else add to our window + if (pGroup) + pGroup->AddControl(pGUIControl); + else + AddControl(pGUIControl); + // if the new control is a group, then add it's controls + if (pGUIControl->IsGroup()) + { + TiXmlElement *pSubControl = pControl->FirstChildElement("control"); + while (pSubControl) + { + LoadControl(pSubControl, (CGUIControlGroup *)pGUIControl); + pSubControl = pSubControl->NextSiblingElement("control"); + } + } + } +} + +void CGUIWindow::OnWindowLoaded() +{ + DynamicResourceAlloc(true); +} + +void CGUIWindow::CenterWindow() +{ + if (m_bRelativeCoords) + { + m_posX = (g_settings.m_ResInfo[m_coordsRes].iWidth - GetWidth()) / 2; + m_posY = (g_settings.m_ResInfo[m_coordsRes].iHeight - GetHeight()) / 2; + } +} + +void CGUIWindow::Render() +{ + // If we're rendering from a different thread, then we should wait for the main + // app thread to finish AllocResources(), as dynamic resources (images in particular) + // will try and be allocated from 2 different threads, which causes nasty things + // to occur. + if (!m_bAllocated) return; + + // find our origin point + float posX = m_posX; + float posY = m_posY; + for (unsigned int i = 0; i < m_origins.size(); i++) + { + // no condition implies true + if (!m_origins[i].condition || g_infoManager.GetBool(m_origins[i].condition, GetID())) + { // found origin + posX = m_origins[i].x; + posY = m_origins[i].y; + break; + } + } + g_graphicsContext.SetRenderingResolution(m_coordsRes, posX, posY, m_needsScaling); + if (m_hasCamera) + g_graphicsContext.SetCameraPosition(m_camera); + + DWORD currentTime = timeGetTime(); + // render our window animation - returns false if it needs to stop rendering + if (!RenderAnimation(currentTime)) + return; + + for (iControls i = m_children.begin(); i != m_children.end(); ++i) + { + CGUIControl *pControl = *i; + if (pControl) + { + GUIPROFILER_VISIBILITY_BEGIN(pControl); + pControl->UpdateVisibility(); + GUIPROFILER_VISIBILITY_END(pControl); + pControl->DoRender(currentTime); + } + } + if (CGUIControlProfiler::IsRunning()) CGUIControlProfiler::Instance().EndFrame(); + m_hasRendered = true; +} + +void CGUIWindow::Close(bool forceClose) +{ + CLog::Log(LOGERROR,"%s - should never be called on the base class!", __FUNCTION__); +} + +bool CGUIWindow::OnAction(const CAction &action) +{ + if (action.id == ACTION_MOUSE) + return OnMouseAction(); + + CGUIControl *focusedControl = GetFocusedControl(); + if (focusedControl) + return focusedControl->OnAction(action); + + // no control has focus? + // set focus to the default control then + CGUIMessage msg(GUI_MSG_SETFOCUS, GetID(), m_defaultControl); + OnMessage(msg); + return false; +} + +// OnMouseAction - called by OnAction() +bool CGUIWindow::OnMouseAction() +{ + // we need to convert the mouse coordinates to window coordinates + float posX = m_posX; + float posY = m_posY; + for (unsigned int i = 0; i < m_origins.size(); i++) + { + // no condition implies true + if (!m_origins[i].condition || g_infoManager.GetBool(m_origins[i].condition, GetID())) + { // found origin + posX = m_origins[i].x; + posY = m_origins[i].y; + break; + } + } + g_graphicsContext.SetScalingResolution(m_coordsRes, posX, posY, m_needsScaling); + CPoint mousePoint(g_Mouse.GetLocation()); + g_graphicsContext.InvertFinalCoords(mousePoint.x, mousePoint.y); + m_transform.InverseTransformPosition(mousePoint.x, mousePoint.y); + + bool bHandled = false; + // check if we have exclusive access + if (g_Mouse.GetExclusiveWindowID() == GetID()) + { // we have exclusive access to the mouse... + CGUIControl *pControl = (CGUIControl *)GetControl(g_Mouse.GetExclusiveControlID()); + if (pControl) + { // this control has exclusive access to the mouse + HandleMouse(pControl, mousePoint + g_Mouse.GetExclusiveOffset()); + return true; + } + } + + // run through the controls, and unfocus all those that aren't under the pointer, + for (iControls i = m_children.begin(); i != m_children.end(); ++i) + { + CGUIControl *pControl = *i; + pControl->UnfocusFromPoint(mousePoint); + } + // and find which one is under the pointer + // go through in reverse order to make sure we start with the ones on top + bool controlUnderPointer(false); + for (vector<CGUIControl *>::reverse_iterator i = m_children.rbegin(); i != m_children.rend(); ++i) + { + CGUIControl *pControl = *i; + CGUIControl *focusableControl = NULL; + CPoint controlPoint; + if (pControl->CanFocusFromPoint(mousePoint, &focusableControl, controlPoint)) + { + controlUnderPointer = focusableControl->OnMouseOver(controlPoint); + bHandled = HandleMouse(focusableControl, controlPoint); + if (bHandled || controlUnderPointer) + break; + } + } + if (!bHandled) + { // haven't handled this action - call the window message handlers + bHandled = OnMouse(mousePoint); + } + // and unfocus everything otherwise + if (!controlUnderPointer) + m_focusedControl = 0; + + return bHandled; +} + +// Handles any mouse actions that are not handled by a control +// default is to go back a window on a right click. +// This function should be overridden for other windows +bool CGUIWindow::OnMouse(const CPoint &point) +{ + if (g_Mouse.bClick[MOUSE_RIGHT_BUTTON]) + { // no control found to absorb this click - go to previous menu + CAction action; + action.id = ACTION_PREVIOUS_MENU; + return OnAction(action); + } + return false; +} + +bool CGUIWindow::HandleMouse(CGUIControl *pControl, const CPoint &point) +{ + if (g_Mouse.bClick[MOUSE_LEFT_BUTTON]) + { // Left click + return pControl->OnMouseClick(MOUSE_LEFT_BUTTON, point); + } + else if (g_Mouse.bClick[MOUSE_RIGHT_BUTTON]) + { // Right click + return pControl->OnMouseClick(MOUSE_RIGHT_BUTTON, point); + } + else if (g_Mouse.bClick[MOUSE_MIDDLE_BUTTON]) + { // Middle click + return pControl->OnMouseClick(MOUSE_MIDDLE_BUTTON, point); + } + else if (g_Mouse.bDoubleClick[MOUSE_LEFT_BUTTON]) + { // Left double click + return pControl->OnMouseDoubleClick(MOUSE_LEFT_BUTTON, point); + } + else if (g_Mouse.bHold[MOUSE_LEFT_BUTTON] && g_Mouse.HasMoved(true)) + { // Mouse Drag + return pControl->OnMouseDrag(g_Mouse.GetLastMove(), point); + } + else if (g_Mouse.GetWheel()) + { // Mouse wheel + return pControl->OnMouseWheel(g_Mouse.GetWheel(), point); + } + // no mouse stuff done other than movement + return false; +} + +/// \brief Called on window open. +/// * Restores the control state(s) +/// * Sets initial visibility of controls +/// * Queue WindowOpen animation +/// * Set overlay state +/// Override this function and do any window-specific initialisation such +/// as filling control contents and setting control focus before +/// calling the base method. +void CGUIWindow::OnInitWindow() +{ + // set our rendered state + m_hasRendered = false; + ResetAnimations(); // we need to reset our animations as those windows that don't dynamically allocate + // need their anims reset. An alternative solution is turning off all non-dynamic + // allocation (which in some respects may be nicer, but it kills hdd spindown and the like) + + // set our initial control visibility before restoring control state and + // focusing the default control, and again afterward to make sure that + // any controls that depend on the state of the focused control (and or on + // control states) are active. + SetInitialVisibility(); + RestoreControlStates(); + SetInitialVisibility(); + QueueAnimation(ANIM_TYPE_WINDOW_OPEN); + m_gWindowManager.ShowOverlay(m_overlayState); + + if (!m_manualRunActions) + { + RunLoadActions(); + } +} + +// Called on window close. +// * Executes the window close animation(s) +// * Saves control state(s) +// Override this function and call the base class before doing any dynamic memory freeing +void CGUIWindow::OnDeinitWindow(int nextWindowID) +{ + if (!m_manualRunActions) + { + RunUnloadActions(); + } + + if (nextWindowID != WINDOW_FULLSCREEN_VIDEO) + { + // Dialog animations are handled in Close() rather than here + if (HasAnimation(ANIM_TYPE_WINDOW_CLOSE) && !IsDialog() && IsActive()) + { + // Perform the window out effect + QueueAnimation(ANIM_TYPE_WINDOW_CLOSE); + while (IsAnimating(ANIM_TYPE_WINDOW_CLOSE)) + { + m_gWindowManager.Process(true); + } + } + } + SaveControlStates(); +} + +bool CGUIWindow::OnMessage(CGUIMessage& message) +{ + switch ( message.GetMessage() ) + { + case GUI_MSG_WINDOW_INIT: + { + CLog::Log(LOGDEBUG, "------ Window Init (%s) ------", m_xmlFile.c_str()); + if (m_dynamicResourceAlloc || !m_bAllocated) AllocResources(); + OnInitWindow(); + return true; + } + break; + + case GUI_MSG_WINDOW_DEINIT: + { + CLog::Log(LOGDEBUG, "------ Window Deinit (%s) ------", m_xmlFile.c_str()); + OnDeinitWindow(message.GetParam1()); + // now free the window + if (m_dynamicResourceAlloc) FreeResources(); + return true; + } + break; + + case GUI_MSG_CLICKED: + { + // a specific control was clicked + CLICK_EVENT clickEvent = m_mapClickEvents[ message.GetSenderId() ]; + + // determine if there are any handlers for this event + if (clickEvent.HasAHandler()) + { + // fire the message to all handlers + clickEvent.Fire(message); + } + break; + } + + case GUI_MSG_SELCHANGED: + { + // a selection within a specific control has changed + SELECTED_EVENT selectedEvent = m_mapSelectedEvents[ message.GetSenderId() ]; + + // determine if there are any handlers for this event + if (selectedEvent.HasAHandler()) + { + // fire the message to all handlers + selectedEvent.Fire(message); + } + break; + } + case GUI_MSG_FOCUSED: + { // a control has been focused + if (HasID(message.GetSenderId())) + { + m_focusedControl = message.GetControlId(); + return true; + } + break; + } + case GUI_MSG_LOSTFOCUS: + { + // nothing to do at the window level when we lose focus + return true; + } + case GUI_MSG_MOVE: + { + if (HasID(message.GetSenderId())) + return OnMove(message.GetControlId(), message.GetParam1()); + break; + } + case GUI_MSG_SETFOCUS: + { +// CLog::Log(LOGDEBUG,"set focus to control:%i window:%i (%i)\n", message.GetControlId(),message.GetSenderId(), GetID()); + if ( message.GetControlId() ) + { + // first unfocus the current control + CGUIControl *control = GetFocusedControl(); + if (control) + { + CGUIMessage msgLostFocus(GUI_MSG_LOSTFOCUS, GetID(), control->GetID(), message.GetControlId()); + control->OnMessage(msgLostFocus); + } + + // get the control to focus + CGUIControl* pFocusedControl = GetFirstFocusableControl(message.GetControlId()); + if (!pFocusedControl) pFocusedControl = (CGUIControl *)GetControl(message.GetControlId()); + + // and focus it + if (pFocusedControl) + return pFocusedControl->OnMessage(message); + } + return true; + } + break; + case GUI_MSG_NOTIFY_ALL: + { + // only process those notifications that come from this window, or those intended for every window + if (HasID(message.GetSenderId()) || !message.GetSenderId()) + { + if (message.GetParam1() == GUI_MSG_PAGE_CHANGE || + message.GetParam1() == GUI_MSG_REFRESH_THUMBS || + message.GetParam1() == GUI_MSG_REFRESH_LIST) + { // alter the message accordingly, and send to all controls + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + { + CGUIControl *control = *it; + CGUIMessage msg(message.GetParam1(), message.GetControlId(), control->GetID(), message.GetParam2()); + control->OnMessage(msg); + } + } + if (message.GetParam1() == GUI_MSG_INVALIDATE) + { + SetInvalid(); + return true; + } + } + } + break; + } + + return SendControlMessage(message); +} + +void CGUIWindow::AllocResources(bool forceLoad /*= FALSE */) +{ + CSingleLock lock(g_graphicsContext); + + LARGE_INTEGER start; + QueryPerformanceCounter(&start); + + // load skin xml file + bool bHasPath=false; + if (m_xmlFile.Find("\\") > -1 || m_xmlFile.Find("/") > -1 ) + bHasPath = true; + if (m_xmlFile.size() && (forceLoad || m_loadOnDemand || !m_windowLoaded)) + Load(m_xmlFile,bHasPath); + + LARGE_INTEGER slend; + QueryPerformanceCounter(&slend); + + // and now allocate resources + CGUIControlGroup::AllocResources(); + + LARGE_INTEGER end, freq; + QueryPerformanceCounter(&end); + QueryPerformanceFrequency(&freq); + CLog::Log(LOGDEBUG,"Alloc resources: %.2fms (%.2f ms skin load)", 1000.f * (end.QuadPart - start.QuadPart) / freq.QuadPart, 1000.f * (slend.QuadPart - start.QuadPart) / freq.QuadPart); + + m_bAllocated = true; +} + +void CGUIWindow::FreeResources(bool forceUnload /*= FALSE */) +{ + m_bAllocated = false; + CGUIControlGroup::FreeResources(); + //g_TextureManager.Dump(); + // unload the skin + if (m_loadOnDemand || forceUnload) ClearAll(); +} + +void CGUIWindow::DynamicResourceAlloc(bool bOnOff) +{ + m_dynamicResourceAlloc = bOnOff; + CGUIControlGroup::DynamicResourceAlloc(bOnOff); +} + +void CGUIWindow::ClearAll() +{ + OnWindowUnload(); + CGUIControlGroup::ClearAll(); + m_windowLoaded = false; + m_dynamicResourceAlloc = true; +} + +bool CGUIWindow::Initialize() +{ + return Load(m_xmlFile); +} + +void CGUIWindow::SetInitialVisibility() +{ + // reset our info manager caches + g_infoManager.ResetCache(); + CGUIControlGroup::SetInitialVisibility(); +} + +bool CGUIWindow::IsActive() const +{ + return m_gWindowManager.IsWindowActive(GetID()); +} + +bool CGUIWindow::CheckAnimation(ANIMATION_TYPE animType) +{ + // special cases first + if (animType == ANIM_TYPE_WINDOW_CLOSE) + { + if (!m_bAllocated || !m_hasRendered) // can't render an animation if we aren't allocated or haven't rendered + return false; + // make sure we update our visibility prior to queuing the window close anim + for (unsigned int i = 0; i < m_children.size(); i++) + m_children[i]->UpdateVisibility(); + } + return true; +} + +bool CGUIWindow::IsAnimating(ANIMATION_TYPE animType) +{ + if (!m_animationsEnabled) + return false; + return CGUIControlGroup::IsAnimating(animType); +} + +bool CGUIWindow::RenderAnimation(DWORD time) +{ + g_graphicsContext.ResetWindowTransform(); + if (m_animationsEnabled) + CGUIControlGroup::Animate(time); + else + m_transform.Reset(); + return true; +} + +void CGUIWindow::DisableAnimations() +{ + m_animationsEnabled = false; +} + +// returns true if the control group with id groupID has controlID as +// its focused control +bool CGUIWindow::ControlGroupHasFocus(int groupID, int controlID) +{ + // 1. Run through and get control with groupID (assume unique) + // 2. Get it's selected item. + CGUIControl *group = GetFirstFocusableControl(groupID); + if (!group) group = (CGUIControl *)GetControl(groupID); + + if (group && group->IsGroup()) + { + if (controlID == 0) + { // just want to know if the group is focused + return group->HasFocus(); + } + else + { + CGUIMessage message(GUI_MSG_ITEM_SELECTED, GetID(), group->GetID()); + group->OnMessage(message); + return (controlID == (int) message.GetParam1()); + } + } + return false; +} + +void CGUIWindow::SaveControlStates() +{ + ResetControlStates(); + if (!m_defaultAlways) + m_lastControlID = GetFocusedControlID(); + for (iControls it = m_children.begin(); it != m_children.end(); ++it) + (*it)->SaveStates(m_controlStates); +} + +void CGUIWindow::RestoreControlStates() +{ + for (vector<CControlState>::iterator it = m_controlStates.begin(); it != m_controlStates.end(); ++it) + { + CGUIMessage message(GUI_MSG_ITEM_SELECT, GetID(), (*it).m_id, (*it).m_data); + OnMessage(message); + } + int focusControl = (!m_defaultAlways && m_lastControlID) ? m_lastControlID : m_defaultControl; + SET_CONTROL_FOCUS(focusControl, 0); +} + +void CGUIWindow::ResetControlStates() +{ + m_lastControlID = 0; + m_focusedControl = 0; + m_controlStates.clear(); +} + +bool CGUIWindow::OnMove(int fromControl, int moveAction) +{ + const CGUIControl *control = GetFirstFocusableControl(fromControl); + if (!control) control = GetControl(fromControl); + if (!control) + { // no current control?? + CLog::Log(LOGERROR, "Unable to find control %i in window %u", + fromControl, GetID()); + return false; + } + vector<int> moveHistory; + int nextControl = fromControl; + while (control) + { // grab the next control direction + moveHistory.push_back(nextControl); + nextControl = control->GetNextControl(moveAction); + // check our history - if the nextControl is in it, we can't focus it + for (unsigned int i = 0; i < moveHistory.size(); i++) + { + if (nextControl == moveHistory[i]) + return false; // no control to focus so do nothing + } + control = GetFirstFocusableControl(nextControl); + if (control) + break; // found a focusable control + control = GetControl(nextControl); // grab the next control and try again + } + if (!control) + return false; // no control to focus + // if we get here we have our new control so focus it (and unfocus the current control) + SET_CONTROL_FOCUS(nextControl, 0); + return true; +} + +void CGUIWindow::SetDefaults() +{ + m_renderOrder = 0; + m_defaultAlways = false; + m_defaultControl = 0; + m_bRelativeCoords = false; + m_posX = m_posY = m_width = m_height = 0; + m_overlayState = OVERLAY_STATE_PARENT_WINDOW; // Use parent or previous window's state + m_visibleCondition = 0; + m_previousWindow = WINDOW_INVALID; + m_animations.clear(); + m_origins.clear(); + m_hasCamera = false; + m_animationsEnabled = true; +} + +FRECT CGUIWindow::GetScaledBounds() const +{ + CSingleLock lock(g_graphicsContext); + g_graphicsContext.SetScalingResolution(m_coordsRes, m_posX, m_posY, m_needsScaling); + FRECT rect = {0, 0, m_width, m_height}; + float z = 0; + g_graphicsContext.ScaleFinalCoords(rect.left, rect.top, z); + g_graphicsContext.ScaleFinalCoords(rect.right, rect.bottom, z); + return rect; +} + +void CGUIWindow::OnEditChanged(int id, CStdString &text) +{ + CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), id); + OnMessage(msg); + text = msg.GetLabel(); +} + +bool CGUIWindow::SendMessage(int message, int id, int param1 /* = 0*/, int param2 /* = 0*/) +{ + CGUIMessage msg(message, GetID(), id, param1, param2); + return OnMessage(msg); +} + +#ifdef _DEBUG +void CGUIWindow::DumpTextureUse() +{ + CLog::Log(LOGDEBUG, "%s for window %u", __FUNCTION__, GetID()); + CGUIControlGroup::DumpTextureUse(); +} +#endif + +void CGUIWindow::ChangeButtonToEdit(int id, bool singleLabel /* = false*/) +{ +#ifdef PRE_SKIN_VERSION_9_10_COMPATIBILITY + CGUIControl *name = (CGUIControl *)GetControl(id); + if (name && name->GetControlType() == CGUIControl::GUICONTROL_BUTTON) + { // change it to an edit control + CGUIEditControl *edit = new CGUIEditControl(*(const CGUIButtonControl *)name); + if (edit) + { + if (singleLabel) + edit->SetLabel(""); + InsertControl(edit, name); + RemoveControl(name); + name->FreeResources(); + delete name; + } + } +#endif +} + +void CGUIWindow::SetProperty(const CStdString &strKey, const char *strValue) +{ + m_mapProperties[strKey] = strValue; +} + +void CGUIWindow::SetProperty(const CStdString &strKey, const CStdString &strValue) +{ + m_mapProperties[strKey] = strValue; +} + +void CGUIWindow::SetProperty(const CStdString &strKey, int nVal) +{ + CStdString strVal; + strVal.Format("%d",nVal); + SetProperty(strKey, strVal); +} + +void CGUIWindow::SetProperty(const CStdString &strKey, bool bVal) +{ + SetProperty(strKey, bVal?"1":"0"); +} + +void CGUIWindow::SetProperty(const CStdString &strKey, double dVal) +{ + CStdString strVal; + strVal.Format("%f",dVal); + SetProperty(strKey, strVal); +} + +CStdString CGUIWindow::GetProperty(const CStdString &strKey) const +{ + std::map<CStdString,CStdString,icompare>::const_iterator iter = m_mapProperties.find(strKey); + if (iter == m_mapProperties.end()) + return ""; + + return iter->second; +} + +int CGUIWindow::GetPropertyInt(const CStdString &strKey) const +{ + return atoi(GetProperty(strKey).c_str()) ; +} + +bool CGUIWindow::GetPropertyBOOL(const CStdString &strKey) const +{ + return GetProperty(strKey) == "1"; +} + +double CGUIWindow::GetPropertyDouble(const CStdString &strKey) const +{ + return atof(GetProperty(strKey).c_str()) ; +} + +bool CGUIWindow::HasProperty(const CStdString &strKey) const +{ + std::map<CStdString,CStdString,icompare>::const_iterator iter = m_mapProperties.find(strKey); + if (iter == m_mapProperties.end()) + return FALSE; + + return TRUE; + } + +void CGUIWindow::ClearProperty(const CStdString &strKey) +{ + std::map<CStdString,CStdString,icompare>::iterator iter = m_mapProperties.find(strKey); + if (iter != m_mapProperties.end()) + m_mapProperties.erase(iter); +} + +void CGUIWindow::ClearProperties() +{ + m_mapProperties.clear(); +} + +void CGUIWindow::RunActions(std::vector<CGUIActionDescriptor>& actions) +{ + vector<CGUIActionDescriptor> tempActions = actions; + + // and execute our actions + for (unsigned int i = 0; i < tempActions.size(); i++) + { + CGUIMessage message(GUI_MSG_EXECUTE, 0, GetID()); + message.SetAction(tempActions[i]); + g_graphicsContext.SendMessage(message); + } +} + +void CGUIWindow::SetRunActionsManually() +{ + m_manualRunActions = true; +} + +void CGUIWindow::RunLoadActions() +{ + RunActions(m_loadActions); +} + +void CGUIWindow::RunUnloadActions() +{ + RunActions(m_unloadActions); +} diff --git a/guilib/GUIWindow.h b/guilib/GUIWindow.h new file mode 100644 index 0000000000..868be691ff --- /dev/null +++ b/guilib/GUIWindow.h @@ -0,0 +1,243 @@ +/*! +\file GUIWindow.h +\brief +*/ + +#ifndef GUILIB_GUIWINDOW_H +#define GUILIB_GUIWINDOW_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIControlGroup.h" +#include "boost/shared_ptr.hpp" + +class CFileItem; typedef boost::shared_ptr<CFileItem> CFileItemPtr; + +#include "GUICallback.h" // for GUIEvent + +#include <map> +#include <vector> + +#define ON_CLICK_MESSAGE(i,c,m) \ +{ \ + GUIEventHandler<c, CGUIMessage&> clickHandler(this, &m); \ + m_mapClickEvents[i] = clickHandler; \ +} \ + +#define ON_SELECTED_MESSAGE(i,c,m) \ +{ \ + GUIEventHandler<c, CGUIMessage&> selectedHandler(this, &m); \ + m_mapSelectedEvents[i] = selectedHandler; \ +} \ + +// forward +class TiXmlNode; +class TiXmlElement; +class TiXmlDocument; + +class COrigin +{ +public: + COrigin() + { + x = y = 0; + condition = 0; + }; + float x; + float y; + int condition; +}; + +/*! + \ingroup winmsg + \brief + */ +class CGUIWindow : public CGUIControlGroup +{ +public: + enum WINDOW_TYPE { WINDOW = 0, MODAL_DIALOG, MODELESS_DIALOG, BUTTON_MENU, SUB_MENU }; + + CGUIWindow(int id, const CStdString &xmlFile); + virtual ~CGUIWindow(void); + + bool Initialize(); // loads the window + bool Load(const CStdString& strFileName, bool bContainsPath = false); + + void CenterWindow(); + virtual void Render(); + + // Close should never be called on this base class (only on derivatives) - its here so that window-manager can use a general close + virtual void Close(bool forceClose = false); + + // OnAction() is called by our window manager. We should process any messages + // that should be handled at the window level in the derived classes, and any + // unhandled messages should be dropped through to here where we send the message + // on to the currently focused control. Returns true if the action has been handled + // and does not need to be passed further down the line (to our global action handlers) + virtual bool OnAction(const CAction &action); + + virtual bool OnMouse(const CPoint &point); + bool HandleMouse(CGUIControl *pControl, const CPoint &point); + bool OnMove(int fromControl, int moveAction); + virtual bool OnMessage(CGUIMessage& message); + + bool ControlGroupHasFocus(int groupID, int controlID); + virtual bool HasID(int id) { return (id >= m_controlID && id < m_controlID + m_idRange); }; + void SetIDRange(int range) { m_idRange = range; }; + int GetIDRange() const { return m_idRange; }; + int GetPreviousWindow() { return m_previousWindow; }; + FRECT GetScaledBounds() const; + virtual void ClearAll(); + virtual void AllocResources(bool forceLoad = false); + virtual void FreeResources(bool forceUnLoad = false); + virtual void DynamicResourceAlloc(bool bOnOff); + virtual bool IsDialog() const { return false; }; + virtual bool IsDialogRunning() const { return false; }; + virtual bool IsModalDialog() const { return false; }; + virtual bool IsMediaWindow() const { return false; }; + virtual bool HasListItems() const { return false; }; + virtual CFileItemPtr GetCurrentListItem(int offset = 0) { return CFileItemPtr(); }; + virtual int GetViewContainerID() const { return 0; }; + virtual bool IsActive() const; + void SetCoordsRes(RESOLUTION res) { m_coordsRes = res; }; + RESOLUTION GetCoordsRes() const { return m_coordsRes; }; + void SetXMLFile(const CStdString &xmlFile) { m_xmlFile = xmlFile; }; + const CStdString &GetXMLFile() const { return m_xmlFile; }; + void LoadOnDemand(bool loadOnDemand) { m_loadOnDemand = loadOnDemand; }; + bool GetLoadOnDemand() { return m_loadOnDemand; } + int GetRenderOrder() { return m_renderOrder; }; + virtual void SetInitialVisibility(); + + enum OVERLAY_STATE { OVERLAY_STATE_PARENT_WINDOW=0, OVERLAY_STATE_SHOWN, OVERLAY_STATE_HIDDEN }; + + OVERLAY_STATE GetOverlayState() const { return m_overlayState; }; + + virtual bool IsAnimating(ANIMATION_TYPE animType); + void DisableAnimations(); + + virtual void ResetControlStates(); + + void SetRunActionsManually(); + void RunLoadActions(); + void RunUnloadActions(); + + bool HasProperty(const CStdString &strKey) const; + void SetProperty(const CStdString &strKey, const char *strValue); + void SetProperty(const CStdString &strKey, const CStdString &strValue); + void SetProperty(const CStdString &strKey, int nVal); + void SetProperty(const CStdString &strKey, bool bVal); + void SetProperty(const CStdString &strKey, double dVal); + + CStdString GetProperty(const CStdString &strKey) const; + int GetPropertyInt(const CStdString &strKey) const; + bool GetPropertyBOOL(const CStdString &strKey) const; + double GetPropertyDouble(const CStdString &strKey) const; + + void ClearProperties(); + void ClearProperty(const CStdString &strKey); + +#ifdef _DEBUG + void DumpTextureUse(); +#endif + + bool HasSaveLastControl() const { return !m_defaultAlways; }; + +protected: + virtual bool LoadXML(const CStdString& strPath, const CStdString &strLowerPath); ///< Loads from the given file + bool Load(TiXmlDocument &xmlDoc); ///< Loads from the given XML document + virtual void LoadAdditionalTags(TiXmlElement *root) {}; ///< Load additional information from the XML document + + virtual void SetDefaults(); + virtual void OnWindowUnload() {} + virtual void OnWindowLoaded(); + virtual void OnInitWindow(); + virtual void OnDeinitWindow(int nextWindowID); + virtual bool OnMouseAction(); + virtual bool RenderAnimation(DWORD time); + virtual bool CheckAnimation(ANIMATION_TYPE animType); + + CAnimation *GetAnimation(ANIMATION_TYPE animType, bool checkConditions = true); + + // control state saving on window close + virtual void SaveControlStates(); + virtual void RestoreControlStates(); + + // methods for updating controls and sending messages + void OnEditChanged(int id, CStdString &text); + bool SendMessage(int message, int id, int param1 = 0, int param2 = 0); + + typedef GUIEvent<CGUIMessage&> CLICK_EVENT; + typedef std::map<int, CLICK_EVENT> MAPCONTROLCLICKEVENTS; + MAPCONTROLCLICKEVENTS m_mapClickEvents; + + typedef GUIEvent<CGUIMessage&> SELECTED_EVENT; + typedef std::map<int, SELECTED_EVENT> MAPCONTROLSELECTEDEVENTS; + MAPCONTROLSELECTEDEVENTS m_mapSelectedEvents; + + void LoadControl(TiXmlElement* pControl, CGUIControlGroup *pGroup); + +//#ifdef PRE_SKIN_VERSION_9_10_COMPATIBILITY + void ChangeButtonToEdit(int id, bool singleLabel = false); +//#endif + + void RunActions(std::vector<CGUIActionDescriptor>& actions); + + int m_idRange; + bool m_bRelativeCoords; + OVERLAY_STATE m_overlayState; + RESOLUTION m_coordsRes; // resolution that the window coordinates are in. + bool m_needsScaling; + CStdString m_xmlFile; // xml file to load + bool m_windowLoaded; // true if the window's xml file has been loaded + bool m_loadOnDemand; // true if the window should be loaded only as needed + bool m_isDialog; // true if we have a dialog, false otherwise. + bool m_dynamicResourceAlloc; + + int m_renderOrder; // for render order of dialogs + + std::vector<COrigin> m_origins; // positions of dialogs depending on base window + + // control states + int m_lastControlID; + std::vector<CControlState> m_controlStates; + int m_previousWindow; + + bool m_animationsEnabled; + struct icompare + { + bool operator()(const CStdString &s1, const CStdString &s2) const + { + return s1.CompareNoCase(s2) < 0; + } + }; + + std::map<CStdString, CStdString, icompare> m_mapProperties; + + std::vector<CGUIActionDescriptor> m_loadActions; + std::vector<CGUIActionDescriptor> m_unloadActions; + + bool m_manualRunActions; +}; + +#endif diff --git a/guilib/GUIWindowManager.cpp b/guilib/GUIWindowManager.cpp new file mode 100644 index 0000000000..e2ec0761d8 --- /dev/null +++ b/guilib/GUIWindowManager.cpp @@ -0,0 +1,891 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIWindowManager.h" +#include "GUIAudioManager.h" +#include "GUIDialog.h" +#include "Application.h" +#include "GUIPassword.h" +#include "utils/GUIInfoManager.h" +#include "Util.h" +#include "GUISettings.h" +#include "Settings.h" + +using namespace std; + +CGUIWindowManager m_gWindowManager; + +CGUIWindowManager::CGUIWindowManager(void) +{ + InitializeCriticalSection(&m_critSection); + + m_pCallback = NULL; + m_bShowOverlay = true; +} + +CGUIWindowManager::~CGUIWindowManager(void) +{ + DeleteCriticalSection(&m_critSection); +} + +void CGUIWindowManager::Initialize() +{ + g_graphicsContext.setMessageSender(this); + LoadNotOnDemandWindows(); +} + +bool CGUIWindowManager::SendMessage(CGUIMessage& message) +{ + bool handled = false; +// CLog::Log(LOGDEBUG,"SendMessage: mess=%d send=%d control=%d param1=%d", message.GetMessage(), message.GetSenderId(), message.GetControlId(), message.GetParam1()); + // Send the message to all none window targets + for (int i = 0; i < (int) m_vecMsgTargets.size(); i++) + { + IMsgTargetCallback* pMsgTarget = m_vecMsgTargets[i]; + + if (pMsgTarget) + { + if (pMsgTarget->OnMessage( message )) handled = true; + } + } + + // A GUI_MSG_NOTIFY_ALL is send to any active modal dialog + // and all windows whether they are active or not + if (message.GetMessage()==GUI_MSG_NOTIFY_ALL) + { + for (rDialog it = m_activeDialogs.rbegin(); it != m_activeDialogs.rend(); ++it) + { + CGUIWindow *dialog = *it; + dialog->OnMessage(message); + } + + for (WindowMap::iterator it = m_mapWindows.begin(); it != m_mapWindows.end(); it++) + { + CGUIWindow *pWindow = (*it).second; + pWindow->OnMessage(message); + } + return true; + } + + // Normal messages are sent to: + // 1. All active modeless dialogs + // 2. The topmost dialog that accepts the message + // 3. The underlying window (only if it is the sender or receiver if a modal dialog is active) + + bool hasModalDialog(false); + bool modalAcceptedMessage(false); + // don't use an iterator for this loop, as some messages mean that m_activeDialogs is altered, + // which will invalidate any iterator + unsigned int topWindow = m_activeDialogs.size(); + while (topWindow) + { + CGUIWindow* dialog = m_activeDialogs[--topWindow]; + if (!modalAcceptedMessage && dialog->IsModalDialog()) + { // modal window + hasModalDialog = true; + if (!modalAcceptedMessage && dialog->OnMessage( message )) + { + modalAcceptedMessage = handled = true; + } + } + else if (!dialog->IsModalDialog()) + { // modeless + if (dialog->OnMessage( message )) + handled = true; + } + } + + // now send to the underlying window + CGUIWindow* window = GetWindow(GetActiveWindow()); + if (window) + { + if (hasModalDialog) + { + // only send the message to the underlying window if it's the recipient + // or sender (or we have no sender) + if (message.GetSenderId() == window->GetID() || + message.GetControlId() == window->GetID() || + message.GetSenderId() == 0 ) + { + if (window->OnMessage(message)) handled = true; + } + } + else + { + if (window->OnMessage(message)) handled = true; + } + } + return handled; +} + +bool CGUIWindowManager::SendMessage(CGUIMessage& message, int window) +{ + CGUIWindow* pWindow = GetWindow(window); + if(pWindow) + return pWindow->OnMessage(message); + else + return false; +} + +void CGUIWindowManager::AddUniqueInstance(CGUIWindow *window) +{ + // increment our instance (upper word of windowID) + // until we get a window we don't have + int instance = 0; + while (GetWindow(window->GetID())) + window->SetID(window->GetID() + (++instance << 16)); + Add(window); +} + +void CGUIWindowManager::Add(CGUIWindow* pWindow) +{ + if (!pWindow) + { + CLog::Log(LOGERROR, "Attempted to add a NULL window pointer to the window manager."); + return; + } + // push back all the windows if there are more than one covered by this class + for (int i = 0; i < pWindow->GetIDRange(); i++) + { + WindowMap::iterator it = m_mapWindows.find(pWindow->GetID() + i); + if (it != m_mapWindows.end()) + { + CLog::Log(LOGERROR, "Error, trying to add a second window with id %u " + "to the window manager", + pWindow->GetID()); + return; + } + m_mapWindows.insert(pair<int, CGUIWindow *>(pWindow->GetID() + i, pWindow)); + } +} + +void CGUIWindowManager::AddCustomWindow(CGUIWindow* pWindow) +{ + Add(pWindow); + m_vecCustomWindows.push_back(pWindow); +} + +void CGUIWindowManager::AddModeless(CGUIWindow* dialog) +{ + // only add the window if it's not already added + for (iDialog it = m_activeDialogs.begin(); it != m_activeDialogs.end(); ++it) + if (*it == dialog) return; + m_activeDialogs.push_back(dialog); +} + +void CGUIWindowManager::Remove(int id) +{ + WindowMap::iterator it = m_mapWindows.find(id); + if (it != m_mapWindows.end()) + { + m_mapWindows.erase(it); + } + else + { + CLog::Log(LOGWARNING, "Attempted to remove window %u " + "from the window manager when it didn't exist", + id); + } +} + +// removes and deletes the window. Should only be called +// from the class that created the window using new. +void CGUIWindowManager::Delete(int id) +{ + CGUIWindow *pWindow = GetWindow(id); + if (pWindow) + { + Remove(id); + delete pWindow; + } +} + +void CGUIWindowManager::PreviousWindow() +{ + // deactivate any window + CLog::Log(LOGDEBUG,"CGUIWindowManager::PreviousWindow: Deactivate"); + int currentWindow = GetActiveWindow(); + CGUIWindow *pCurrentWindow = GetWindow(currentWindow); + if (!pCurrentWindow) + return; // no windows or window history yet + + // check to see whether our current window has a <previouswindow> tag + if (pCurrentWindow->GetPreviousWindow() != WINDOW_INVALID) + { + // TODO: we may need to test here for the + // whether our history should be changed + + // don't reactivate the previouswindow if it is ourselves. + if (currentWindow != pCurrentWindow->GetPreviousWindow()) + ActivateWindow(pCurrentWindow->GetPreviousWindow()); + return; + } + // get the previous window in our stack + if (m_windowHistory.size() < 2) + { // no previous window history yet - check if we should just activate home + if (GetActiveWindow() != WINDOW_INVALID && GetActiveWindow() != WINDOW_HOME) + { + ClearWindowHistory(); + ActivateWindow(WINDOW_HOME); + } + return; + } + m_windowHistory.pop(); + int previousWindow = GetActiveWindow(); + m_windowHistory.push(currentWindow); + + CGUIWindow *pNewWindow = GetWindow(previousWindow); + if (!pNewWindow) + { + CLog::Log(LOGERROR, "Unable to activate the previous window"); + ClearWindowHistory(); + ActivateWindow(WINDOW_HOME); + return; + } + + // ok to go to the previous window now + + // tell our info manager which window we are going to + g_infoManager.SetNextWindow(previousWindow); + + // set our overlay state (enables out animations on window change) + HideOverlay(pNewWindow->GetOverlayState()); + + // deinitialize our window + g_audioManager.PlayWindowSound(pCurrentWindow->GetID(), SOUND_DEINIT); + CGUIMessage msg(GUI_MSG_WINDOW_DEINIT, 0, 0); + pCurrentWindow->OnMessage(msg); + + g_infoManager.SetNextWindow(WINDOW_INVALID); + g_infoManager.SetPreviousWindow(currentWindow); + + // remove the current window off our window stack + m_windowHistory.pop(); + + // ok, initialize the new window + CLog::Log(LOGDEBUG,"CGUIWindowManager::PreviousWindow: Activate new"); + g_audioManager.PlayWindowSound(pNewWindow->GetID(), SOUND_INIT); + CGUIMessage msg2(GUI_MSG_WINDOW_INIT, 0, 0, WINDOW_INVALID, GetActiveWindow()); + pNewWindow->OnMessage(msg2); + + g_infoManager.SetPreviousWindow(WINDOW_INVALID); + return; +} + +void CGUIWindowManager::RefreshWindow() +{ + // deactivate the current window + CGUIWindow *pWindow = GetWindow(GetActiveWindow()); + if (!pWindow) + return; + + CGUIMessage msg(GUI_MSG_WINDOW_DEINIT, 0, 0); + pWindow->OnMessage(msg); + CGUIMessage msg2(GUI_MSG_WINDOW_INIT, 0, 0, WINDOW_INVALID); + pWindow->OnMessage(msg2); +} + +void CGUIWindowManager::ChangeActiveWindow(int newWindow, const CStdString& strPath) +{ + vector<CStdString> params; + if (!strPath.IsEmpty()) + params.push_back(strPath); + ActivateWindow(newWindow, params, true); +} + +void CGUIWindowManager::ActivateWindow(int iWindowID, const CStdString& strPath) +{ + vector<CStdString> params; + if (!strPath.IsEmpty()) + params.push_back(strPath); + ActivateWindow(iWindowID, params, false); +} + +void CGUIWindowManager::ActivateWindow(int iWindowID, const vector<CStdString>& params, bool swappingWindows) +{ + if (!g_application.IsCurrentThread()) + { + // make sure graphics lock is not held + int nCount = ExitCriticalSection(g_graphicsContext); + g_application.getApplicationMessenger().ActivateWindow(iWindowID, params, swappingWindows); + RestoreCriticalSection(g_graphicsContext, nCount); + } + else + ActivateWindow_Internal(iWindowID, params, swappingWindows); +} + +void CGUIWindowManager::ActivateWindow_Internal(int iWindowID, const vector<CStdString>& params, bool swappingWindows) +{ + bool passParams = true; + // translate virtual windows + // virtual music window which returns the last open music window (aka the music start window) + if (iWindowID == WINDOW_MUSIC) + { + iWindowID = g_stSettings.m_iMyMusicStartWindow; + // ensure the music virtual window only returns music files and music library windows + if (iWindowID != WINDOW_MUSIC_NAV) + iWindowID = WINDOW_MUSIC_FILES; + // destination path cannot be used with virtual window + passParams = false; + } + // virtual video window which returns the last open video window (aka the video start window) + if (iWindowID == WINDOW_VIDEOS) + { + iWindowID = g_stSettings.m_iVideoStartWindow; + // ensure the virtual video window only returns video windows + if (iWindowID != WINDOW_VIDEO_NAV) + iWindowID = WINDOW_VIDEO_FILES; + // destination path cannot be used with virtual window + passParams = false; + } + // Is the Library enabled? If not, go to Files view. + if (iWindowID == WINDOW_MUSIC_NAV && !g_guiSettings.GetBool("musiclibrary.enabled")) + { + iWindowID = WINDOW_MUSIC_FILES; + passParams = false; + CLog::Log(LOGDEBUG, "Trying to activate Music Library, but its disabled. Switching to Files instead."); + } + if (iWindowID == WINDOW_VIDEO_NAV && !g_guiSettings.GetBool("videolibrary.enabled")) + { + iWindowID = WINDOW_VIDEO_FILES; + passParams = false; + CLog::Log(LOGDEBUG, "Trying to activate Video Library, but its disabled. Switching to Files instead."); + } + + // debug + CLog::Log(LOGDEBUG, "Activating window ID: %i", iWindowID); + + if(!g_passwordManager.CheckMenuLock(iWindowID)) + { + CLog::Log(LOGERROR, "MasterCode is Wrong: Window with id %d will not be loaded! Enter a correct MasterCode!", iWindowID); + return; + } + + // first check existence of the window we wish to activate. + CGUIWindow *pNewWindow = GetWindow(iWindowID); + if (!pNewWindow) + { // nothing to see here - move along + CLog::Log(LOGERROR, "Unable to locate window with id %d. Check skin files", iWindowID - WINDOW_HOME); + return ; + } + else if (pNewWindow->IsDialog()) + { // if we have a dialog, we do a DoModal() rather than activate the window + if (!pNewWindow->IsDialogRunning()) + ((CGUIDialog *)pNewWindow)->DoModal(iWindowID, (passParams && params.size()) ? params[0] : ""); + return; + } + + g_infoManager.SetNextWindow(iWindowID); + + // set our overlay state + HideOverlay(pNewWindow->GetOverlayState()); + + // deactivate any window + int currentWindow = GetActiveWindow(); + CGUIWindow *pWindow = GetWindow(currentWindow); + if (pWindow) + { + // Play the window specific deinit sound + g_audioManager.PlayWindowSound(pWindow->GetID(), SOUND_DEINIT); + CGUIMessage msg(GUI_MSG_WINDOW_DEINIT, 0, 0, iWindowID); + pWindow->OnMessage(msg); + } + g_infoManager.SetNextWindow(WINDOW_INVALID); + + // Add window to the history list (we must do this before we activate it, + // as all messages done in WINDOW_INIT will want to be sent to the new + // topmost window). If we are swapping windows, we pop the old window + // off the history stack + if (swappingWindows && m_windowHistory.size()) + m_windowHistory.pop(); + AddToWindowHistory(iWindowID); + + g_infoManager.SetPreviousWindow(currentWindow); + g_audioManager.PlayWindowSound(pNewWindow->GetID(), SOUND_INIT); + // Send the init message + CGUIMessage msg(GUI_MSG_WINDOW_INIT, 0, 0, currentWindow, iWindowID); + if (passParams) + msg.SetStringParams(params); + pNewWindow->OnMessage(msg); +// g_infoManager.SetPreviousWindow(WINDOW_INVALID); +} + +void CGUIWindowManager::CloseDialogs(bool forceClose) +{ + while (m_activeDialogs.size() > 0) + { + CGUIWindow* win = m_activeDialogs[0]; + win->Close(forceClose); + } +} + +bool CGUIWindowManager::OnAction(const CAction &action) +{ + for (rDialog it = m_activeDialogs.rbegin(); it != m_activeDialogs.rend(); ++it) + { + CGUIWindow *dialog = *it; + if (dialog->IsModalDialog()) + { // we have the topmost modal dialog + if (!dialog->IsAnimating(ANIM_TYPE_WINDOW_CLOSE)) + { + if (dialog->OnAction(action)) + return true; + // dialog didn't want the action - we'd normally return true + // but for some dialogs we want to drop the actions through + if (dialog->GetID() == WINDOW_DIALOG_FULLSCREEN_INFO) + break; + return false; + } + return true; // do nothing with the action until the anim is finished + } + // music or video overlay are handled as a special case, as they're modeless, but we allow + // clicking on them with the mouse. + if (action.id == ACTION_MOUSE && (dialog->GetID() == WINDOW_VIDEO_OVERLAY || + dialog->GetID() == WINDOW_MUSIC_OVERLAY)) + { + if (dialog->OnAction(action)) + return true; + } + } + CGUIWindow* window = GetWindow(GetActiveWindow()); + if (window) + return window->OnAction(action); + return false; +} + +void CGUIWindowManager::Render() +{ + if (!g_application.IsCurrentThread()) + { + // make sure graphics lock is not held + int nCount = ExitCriticalSection(g_graphicsContext); + g_application.getApplicationMessenger().Render(); + RestoreCriticalSection(g_graphicsContext, nCount); + } + else + Render_Internal(); +} + +void CGUIWindowManager::Render_Internal() +{ + CGUIWindow* pWindow = GetWindow(GetActiveWindow()); + if (pWindow) + pWindow->Render(); +} + +bool RenderOrderSortFunction(CGUIWindow *first, CGUIWindow *second) +{ + return first->GetRenderOrder() < second->GetRenderOrder(); +} + +void CGUIWindowManager::RenderDialogs() +{ + // find the window with the lowest render order + vector<CGUIWindow *> renderList = m_activeDialogs; + stable_sort(renderList.begin(), renderList.end(), RenderOrderSortFunction); + + // iterate through and render if they're running + for (iDialog it = renderList.begin(); it != renderList.end(); ++it) + { + if ((*it)->IsDialogRunning()) + (*it)->Render(); + } +} + +CGUIWindow* CGUIWindowManager::GetWindow(int id) const +{ + if (id == WINDOW_INVALID) + { + return NULL; + } + + WindowMap::const_iterator it = m_mapWindows.find(id); + if (it != m_mapWindows.end()) + return (*it).second; + return NULL; +} + +// Shows and hides modeless dialogs as necessary. +void CGUIWindowManager::UpdateModelessVisibility() +{ + for (WindowMap::iterator it = m_mapWindows.begin(); it != m_mapWindows.end(); it++) + { + CGUIWindow *pWindow = (*it).second; + if (pWindow && pWindow->IsDialog() && pWindow->GetVisibleCondition()) + { + if (g_infoManager.GetBool(pWindow->GetVisibleCondition(), GetActiveWindow())) + ((CGUIDialog *)pWindow)->Show(); + else + ((CGUIDialog *)pWindow)->Close(); + } + } +} + +void CGUIWindowManager::Process(bool renderOnly /*= false*/) +{ + if (!g_application.IsCurrentThread()) + { + // make sure graphics lock is not held + DWORD locks = ExitCriticalSection(g_graphicsContext); + g_application.getApplicationMessenger().WindowManagerProcess(renderOnly); + RestoreCriticalSection(g_graphicsContext, locks); + } + else + Process_Internal(renderOnly); +} + +void CGUIWindowManager::Process_Internal(bool renderOnly /*= false*/) +{ + if (m_pCallback) + { + if (!renderOnly) + { + m_pCallback->Process(); + m_pCallback->FrameMove(); + } + m_pCallback->Render(); + } +} + +void CGUIWindowManager::SetCallback(IWindowManagerCallback& callback) +{ + m_pCallback = &callback; +} + +void CGUIWindowManager::DeInitialize() +{ + for (WindowMap::iterator it = m_mapWindows.begin(); it != m_mapWindows.end(); it++) + { + CGUIWindow* pWindow = (*it).second; + if (IsWindowActive(it->first)) + { + pWindow->DisableAnimations(); + CGUIMessage msg(GUI_MSG_WINDOW_DEINIT, 0, 0); + pWindow->OnMessage(msg); + } + pWindow->ResetControlStates(); + pWindow->FreeResources(true); + } + UnloadNotOnDemandWindows(); + + m_vecMsgTargets.erase( m_vecMsgTargets.begin(), m_vecMsgTargets.end() ); + + // destroy our custom windows... + for (int i = 0; i < (int)m_vecCustomWindows.size(); i++) + { + CGUIWindow *pWindow = m_vecCustomWindows[i]; + Remove(pWindow->GetID()); + delete pWindow; + } + + // clear our vectors of windows + m_vecCustomWindows.clear(); + m_activeDialogs.clear(); +} + +/// \brief Route to a window +/// \param pWindow Window to route to +void CGUIWindowManager::RouteToWindow(CGUIWindow* dialog) +{ + // Just to be sure: Unroute this window, + // #we may have routed to it before + RemoveDialog(dialog->GetID()); + + m_activeDialogs.push_back(dialog); +} + +/// \brief Unroute window +/// \param id ID of the window routed +void CGUIWindowManager::RemoveDialog(int id) +{ + for (iDialog it = m_activeDialogs.begin(); it != m_activeDialogs.end(); ++it) + { + if ((*it)->GetID() == id) + { + m_activeDialogs.erase(it); + return; + } + } +} + +bool CGUIWindowManager::HasModalDialog() const +{ + for (ciDialog it = m_activeDialogs.begin(); it != m_activeDialogs.end(); ++it) + { + CGUIWindow *window = *it; + if (window->IsModalDialog()) + { // have a modal window + if (!window->IsAnimating(ANIM_TYPE_WINDOW_CLOSE)) + return true; + } + } + return false; +} + +bool CGUIWindowManager::HasDialogOnScreen() const +{ + return (m_activeDialogs.size() > 0); +} + +/// \brief Get the ID of the top most routed window +/// \return id ID of the window or WINDOW_INVALID if no routed window available +int CGUIWindowManager::GetTopMostModalDialogID() const +{ + for (crDialog it = m_activeDialogs.rbegin(); it != m_activeDialogs.rend(); ++it) + { + CGUIWindow *dialog = *it; + if (dialog->IsModalDialog()) + { // have a modal window + return dialog->GetID(); + } + } + return WINDOW_INVALID; +} + +void CGUIWindowManager::SendThreadMessage(CGUIMessage& message) +{ + ::EnterCriticalSection(&m_critSection ); + + CGUIMessage* msg = new CGUIMessage(message); + m_vecThreadMessages.push_back( pair<CGUIMessage*,int>(msg,0) ); + + ::LeaveCriticalSection(&m_critSection ); +} + +void CGUIWindowManager::SendThreadMessage(CGUIMessage& message, int window) +{ + ::EnterCriticalSection(&m_critSection ); + + CGUIMessage* msg = new CGUIMessage(message); + m_vecThreadMessages.push_back( pair<CGUIMessage*,int>(msg,window) ); + + ::LeaveCriticalSection(&m_critSection ); +} + +void CGUIWindowManager::DispatchThreadMessages() +{ + ::EnterCriticalSection(&m_critSection ); + vector< pair<CGUIMessage*,int> > messages(m_vecThreadMessages); + m_vecThreadMessages.erase(m_vecThreadMessages.begin(), m_vecThreadMessages.end()); + ::LeaveCriticalSection(&m_critSection ); + + while ( messages.size() > 0 ) + { + vector< pair<CGUIMessage*,int> >::iterator it = messages.begin(); + CGUIMessage* pMsg = it->first; + int window = it->second; + // first remove the message from the queue, + // else the message could be processed more then once + it = messages.erase(it); + + if (window) + SendMessage( *pMsg, window ); + else + SendMessage( *pMsg ); + delete pMsg; + } +} + +void CGUIWindowManager::AddMsgTarget( IMsgTargetCallback* pMsgTarget ) +{ + m_vecMsgTargets.push_back( pMsgTarget ); +} + +int CGUIWindowManager::GetActiveWindow() const +{ + if (!m_windowHistory.empty()) + return m_windowHistory.top(); + return WINDOW_INVALID; +} + +// same as GetActiveWindow() except it first grabs dialogs +int CGUIWindowManager::GetFocusedWindow() const +{ + int dialog = GetTopMostModalDialogID(); + if (dialog != WINDOW_INVALID) + return dialog; + + return GetActiveWindow(); +} + +bool CGUIWindowManager::IsWindowActive(int id, bool ignoreClosing /* = true */) const +{ + // mask out multiple instances of the same window + id &= WINDOW_ID_MASK; + if ((GetActiveWindow() & WINDOW_ID_MASK) == id) return true; + // run through the dialogs + for (ciDialog it = m_activeDialogs.begin(); it != m_activeDialogs.end(); ++it) + { + CGUIWindow *window = *it; + if ((window->GetID() & WINDOW_ID_MASK) == id && (!ignoreClosing || !window->IsAnimating(ANIM_TYPE_WINDOW_CLOSE))) + return true; + } + return false; // window isn't active +} + +bool CGUIWindowManager::IsWindowActive(const CStdString &xmlFile, bool ignoreClosing /* = true */) const +{ + CGUIWindow *window = GetWindow(GetActiveWindow()); + if (window && CUtil::GetFileName(window->GetXMLFile()).Equals(xmlFile)) return true; + // run through the dialogs + for (ciDialog it = m_activeDialogs.begin(); it != m_activeDialogs.end(); ++it) + { + CGUIWindow *window = *it; + if (CUtil::GetFileName(window->GetXMLFile()).Equals(xmlFile) && (!ignoreClosing || !window->IsAnimating(ANIM_TYPE_WINDOW_CLOSE))) + return true; + } + return false; // window isn't active +} + +bool CGUIWindowManager::IsWindowVisible(int id) const +{ + return IsWindowActive(id, false); +} + +bool CGUIWindowManager::IsWindowVisible(const CStdString &xmlFile) const +{ + return IsWindowActive(xmlFile, false); +} + +void CGUIWindowManager::LoadNotOnDemandWindows() +{ + for (WindowMap::iterator it = m_mapWindows.begin(); it != m_mapWindows.end(); it++) + { + CGUIWindow *pWindow = (*it).second; + if (!pWindow ->GetLoadOnDemand()) + { + pWindow->FreeResources(true); + pWindow->Initialize(); + } + } +} + +void CGUIWindowManager::UnloadNotOnDemandWindows() +{ + for (WindowMap::iterator it = m_mapWindows.begin(); it != m_mapWindows.end(); it++) + { + CGUIWindow *pWindow = (*it).second; + if (!pWindow->GetLoadOnDemand()) + { + pWindow->FreeResources(true); + } + } +} + +bool CGUIWindowManager::IsOverlayAllowed() const +{ + return m_bShowOverlay; +} + +void CGUIWindowManager::ShowOverlay(CGUIWindow::OVERLAY_STATE state) +{ + if (state != CGUIWindow::OVERLAY_STATE_PARENT_WINDOW) + m_bShowOverlay = state == CGUIWindow::OVERLAY_STATE_SHOWN; +} + +void CGUIWindowManager::HideOverlay(CGUIWindow::OVERLAY_STATE state) +{ + if (state == CGUIWindow::OVERLAY_STATE_HIDDEN) + m_bShowOverlay = false; +} + +void CGUIWindowManager::AddToWindowHistory(int newWindowID) +{ + // Check the window stack to see if this window is in our history, + // and if so, pop all the other windows off the stack so that we + // always have a predictable "Back" behaviour for each window + stack<int> historySave = m_windowHistory; + while (historySave.size()) + { + if (historySave.top() == newWindowID) + break; + historySave.pop(); + } + if (!historySave.empty()) + { // found window in history + m_windowHistory = historySave; + } + else + { // didn't find window in history - add it to the stack + m_windowHistory.push(newWindowID); + } +} + +void CGUIWindowManager::GetActiveModelessWindows(vector<int> &ids) +{ + // run through our modeless windows, and construct a vector of them + // useful for saving and restoring the modeless windows on skin change etc. + for (iDialog it = m_activeDialogs.begin(); it != m_activeDialogs.end(); ++it) + { + if (!(*it)->IsModalDialog()) + ids.push_back((*it)->GetID()); + } +} + +CGUIWindow *CGUIWindowManager::GetTopMostDialog() const +{ + // find the window with the lowest render order + vector<CGUIWindow *> renderList = m_activeDialogs; + stable_sort(renderList.begin(), renderList.end(), RenderOrderSortFunction); + + if (!renderList.size()) + return NULL; + + // return the last window in the list + return *renderList.rbegin(); +} + +bool CGUIWindowManager::IsWindowTopMost(int id) const +{ + CGUIWindow *topMost = GetTopMostDialog(); + if (topMost && (topMost->GetID() & WINDOW_ID_MASK) == id) + return true; + return false; +} + +bool CGUIWindowManager::IsWindowTopMost(const CStdString &xmlFile) const +{ + CGUIWindow *topMost = GetTopMostDialog(); + if (topMost && CUtil::GetFileName(topMost->GetXMLFile()).Equals(xmlFile)) + return true; + return false; +} + +void CGUIWindowManager::ClearWindowHistory() +{ + while (m_windowHistory.size()) + m_windowHistory.pop(); +} + +#ifdef _DEBUG +void CGUIWindowManager::DumpTextureUse() +{ + CGUIWindow* pWindow = GetWindow(GetActiveWindow()); + if (pWindow) + pWindow->DumpTextureUse(); + + for (iDialog it = m_activeDialogs.begin(); it != m_activeDialogs.end(); ++it) + { + if ((*it)->IsDialogRunning()) + (*it)->DumpTextureUse(); + } +} +#endif diff --git a/guilib/GUIWindowManager.h b/guilib/GUIWindowManager.h new file mode 100644 index 0000000000..8120e89562 --- /dev/null +++ b/guilib/GUIWindowManager.h @@ -0,0 +1,142 @@ +/*! +\file GUIWindowManager.h +\brief +*/ + +#ifndef GUILIB_CGUIWindowManager_H +#define GUILIB_CGUIWindowManager_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIWindow.h" +#include "IMsgSenderCallback.h" +#include "IWindowManagerCallback.h" +#include "IMsgTargetCallback.h" + +class CGUIDialog; + +#define WINDOW_ID_MASK 0xffff + +/*! + \ingroup winman + \brief + */ +class CGUIWindowManager: public IMsgSenderCallback +{ +public: + CGUIWindowManager(void); + virtual ~CGUIWindowManager(void); + bool SendMessage(CGUIMessage& message); + bool SendMessage(CGUIMessage& message, int window); + void Initialize(); + void Add(CGUIWindow* pWindow); + void AddUniqueInstance(CGUIWindow *window); + void AddCustomWindow(CGUIWindow* pWindow); + void Remove(int id); + void Delete(int id); + void ActivateWindow(int iWindowID, const CStdString &strPath = ""); + void ChangeActiveWindow(int iNewID, const CStdString &strPath = ""); + void ActivateWindow(int iWindowID, const std::vector<CStdString>& params, bool swappingWindows = false); + void PreviousWindow(); + void RefreshWindow(); + void LoadNotOnDemandWindows(); + void UnloadNotOnDemandWindows(); + + void CloseDialogs(bool forceClose = false); + + // OnAction() runs through our active dialogs and windows and sends the message + // off to the callbacks (application, python, playlist player) and to the + // currently focused window(s). Returns true only if the message is handled. + bool OnAction(const CAction &action); + + void Render(); + void RenderDialogs(); + CGUIWindow* GetWindow(int id) const; + void Process(bool renderOnly = false); + void SetCallback(IWindowManagerCallback& callback); + void DeInitialize(); + + void RouteToWindow(CGUIWindow* dialog); + void AddModeless(CGUIWindow* dialog); + void RemoveDialog(int id); + int GetTopMostModalDialogID() const; + + void SendThreadMessage(CGUIMessage& message); + void SendThreadMessage(CGUIMessage& message, int window); + void DispatchThreadMessages(); + void AddMsgTarget( IMsgTargetCallback* pMsgTarget ); + int GetActiveWindow() const; + int GetFocusedWindow() const; + bool HasModalDialog() const; + bool HasDialogOnScreen() const; + void UpdateModelessVisibility(); + bool IsWindowActive(int id, bool ignoreClosing = true) const; + bool IsWindowVisible(int id) const; + bool IsWindowTopMost(int id) const; + bool IsWindowActive(const CStdString &xmlFile, bool ignoreClosing = true) const; + bool IsWindowVisible(const CStdString &xmlFile) const; + bool IsWindowTopMost(const CStdString &xmlFile) const; + bool IsOverlayAllowed() const; + void ShowOverlay(CGUIWindow::OVERLAY_STATE state); + void GetActiveModelessWindows(std::vector<int> &ids); +#ifdef _DEBUG + void DumpTextureUse(); +#endif +private: + void HideOverlay(CGUIWindow::OVERLAY_STATE state); + void AddToWindowHistory(int newWindowID); + void ClearWindowHistory(); + CGUIWindow *GetTopMostDialog() const; + + friend class CApplicationMessenger; + void ActivateWindow_Internal(int windowID, const std::vector<CStdString> ¶ms, bool swappingWindows); + void Process_Internal(bool renderOnly = false); + void Render_Internal(); + + typedef std::map<int, CGUIWindow *> WindowMap; + WindowMap m_mapWindows; + std::vector <CGUIWindow*> m_vecCustomWindows; + std::vector <CGUIWindow*> m_activeDialogs; + typedef std::vector<CGUIWindow*>::iterator iDialog; + typedef std::vector<CGUIWindow*>::const_iterator ciDialog; + typedef std::vector<CGUIWindow*>::reverse_iterator rDialog; + typedef std::vector<CGUIWindow*>::const_reverse_iterator crDialog; + + std::stack<int> m_windowHistory; + + IWindowManagerCallback* m_pCallback; + std::vector < std::pair<CGUIMessage*,int> > m_vecThreadMessages; + CRITICAL_SECTION m_critSection; + std::vector <IMsgTargetCallback*> m_vecMsgTargets; + + bool m_bShowOverlay; +}; + +/*! + \ingroup winman + \brief + */ +extern CGUIWindowManager m_gWindowManager; +#endif + diff --git a/guilib/GUIWrappingListContainer.cpp b/guilib/GUIWrappingListContainer.cpp new file mode 100644 index 0000000000..80170ca5b6 --- /dev/null +++ b/guilib/GUIWrappingListContainer.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIWrappingListContainer.h" +#include "FileItem.h" +#include "Key.h" +#include "utils/log.h" + +CGUIWrappingListContainer::CGUIWrappingListContainer(int parentID, int controlID, float posX, float posY, float width, float height, ORIENTATION orientation, int scrollTime, int preloadItems, int fixedPosition) + : CGUIBaseContainer(parentID, controlID, posX, posY, width, height, orientation, scrollTime, preloadItems) +{ + m_cursor = fixedPosition; + ControlType = GUICONTAINER_WRAPLIST; + m_type = VIEW_TYPE_LIST; + m_extraItems = 0; +} + +CGUIWrappingListContainer::~CGUIWrappingListContainer(void) +{ +} + +void CGUIWrappingListContainer::UpdatePageControl(int offset) +{ + if (m_pageControl) + { // tell our pagecontrol (scrollbar or whatever) to update (offset it by our cursor position) + CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(), m_pageControl, CorrectOffset(offset, m_cursor)); + SendWindowMessage(msg); + } +} + +bool CGUIWrappingListContainer::OnAction(const CAction &action) +{ + switch (action.id) + { + case ACTION_PAGE_UP: + Scroll(-m_itemsPerPage); + return true; + case ACTION_PAGE_DOWN: + Scroll(m_itemsPerPage); + return true; + // smooth scrolling (for analog controls) + case ACTION_SCROLL_UP: + { + m_analogScrollCount += action.amount1 * action.amount1; + bool handled = false; + while (m_analogScrollCount > 0.4) + { + handled = true; + m_analogScrollCount -= 0.4f; + Scroll(-1); + } + return handled; + } + break; + case ACTION_SCROLL_DOWN: + { + m_analogScrollCount += action.amount1 * action.amount1; + bool handled = false; + while (m_analogScrollCount > 0.4) + { + handled = true; + m_analogScrollCount -= 0.4f; + Scroll(1); + } + return handled; + } + break; + } + return CGUIBaseContainer::OnAction(action); +} + +bool CGUIWrappingListContainer::OnMessage(CGUIMessage& message) +{ + if (message.GetControlId() == GetID() ) + { + if (message.GetMessage() == GUI_MSG_ITEM_SELECT) + { + SelectItem(message.GetParam1()); + return true; + } + else if (message.GetMessage() == GUI_MSG_PAGE_CHANGE) + { + if (message.GetSenderId() == m_pageControl && IsVisible()) + { // offset by our cursor position + message.SetParam1(message.GetParam1() - m_cursor); + } + } + } + return CGUIBaseContainer::OnMessage(message); +} + +bool CGUIWrappingListContainer::MoveUp(bool wrapAround) +{ + Scroll(-1); + return true; +} + +bool CGUIWrappingListContainer::MoveDown(bool wrapAround) +{ + Scroll(+1); + return true; +} + +// scrolls the said amount +void CGUIWrappingListContainer::Scroll(int amount) +{ + ScrollToOffset(m_offset + amount); +} + +void CGUIWrappingListContainer::ValidateOffset() +{ + if (m_itemsPerPage <= (int)m_items.size()) + return; + + // no need to check the range here, but we need to check we have + // more items than slots. + ResetExtraItems(); + if (m_items.size()) + { + unsigned int numItems = m_items.size(); + while (m_items.size() < (unsigned int)m_itemsPerPage) + { + // add additional copies of items, as we require extras at render time + for (unsigned int i = 0; i < numItems; i++) + { + if (m_items[i]->IsFileItem()) + m_items.push_back(CFileItemPtr(new CFileItem(*(CFileItem *)m_items[i].get()))); + else + m_items.push_back(CGUIListItemPtr(new CGUIListItem(*m_items[i]))); + m_extraItems++; + } + } + } +} + +int CGUIWrappingListContainer::CorrectOffset(int offset, int cursor) const +{ + if (m_items.size()) + { + int correctOffset = (offset + cursor) % (int)m_items.size(); + if (correctOffset < 0) correctOffset += m_items.size(); + return correctOffset; + } + return 0; +} + +int CGUIWrappingListContainer::GetSelectedItem() const +{ + if (m_items.size() > m_extraItems) + { + int numItems = (int)(m_items.size() - m_extraItems); + int correctOffset = (m_offset + m_cursor) % numItems; + if (correctOffset < 0) correctOffset += numItems; + return correctOffset; + } + return 0; +} + +bool CGUIWrappingListContainer::SelectItemFromPoint(const CPoint &point) +{ + if (!m_focusedLayout || !m_layout) + return false; + + const float mouse_scroll_speed = 0.05f; + const float mouse_max_amount = 1.0f; // max speed: 1 item per frame + float sizeOfItem = m_layout->Size(m_orientation); + // see if the point is either side of our focused item + float start = m_cursor * sizeOfItem; + float end = start + m_focusedLayout->Size(m_orientation); + float pos = (m_orientation == VERTICAL) ? point.y : point.x; + if (pos < start - 0.5f * sizeOfItem) + { // scroll backward + if (!InsideLayout(m_layout, point)) + return false; + float amount = std::min((start - pos) / sizeOfItem, mouse_max_amount); + m_analogScrollCount += amount * amount * mouse_scroll_speed; + CLog::Log(LOGERROR, "%s: Speed %f", __FUNCTION__, amount); + if (m_analogScrollCount > 1) + { + Scroll(-1); + m_analogScrollCount-=1.0f; + } + return true; + } + else if (pos > end + 0.5f * sizeOfItem) + { // scroll forward + if (!InsideLayout(m_layout, point)) + return false; + + float amount = std::min((pos - end) / sizeOfItem, mouse_max_amount); + m_analogScrollCount += amount * amount * mouse_scroll_speed; + if (m_analogScrollCount > 1) + { + Scroll(1); + m_analogScrollCount-=1.0f; + } + return true; + } + return InsideLayout(m_focusedLayout, point); +} + +void CGUIWrappingListContainer::SelectItem(int item) +{ + if (item >= 0 && item < (int)m_items.size()) + ScrollToOffset(item - m_cursor); +} + +void CGUIWrappingListContainer::ResetExtraItems() +{ + // delete any extra items + if (m_extraItems) + m_items.erase(m_items.begin() + m_items.size() - m_extraItems, m_items.end()); + m_extraItems = 0; +} + +void CGUIWrappingListContainer::Reset() +{ + ResetExtraItems(); + CGUIBaseContainer::Reset(); +} + +int CGUIWrappingListContainer::GetCurrentPage() const +{ + int offset = CorrectOffset(m_offset, m_cursor); + if (offset + m_itemsPerPage - m_cursor >= (int)GetRows()) // last page + return (GetRows() + m_itemsPerPage - 1) / m_itemsPerPage; + return offset / m_itemsPerPage + 1; +} + +void CGUIWrappingListContainer::SetPageControlRange() +{ + if (m_pageControl) + { + CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), m_pageControl, m_itemsPerPage, m_items.size() + m_itemsPerPage - 1); + SendWindowMessage(msg); + } +} diff --git a/guilib/GUIWrappingListContainer.h b/guilib/GUIWrappingListContainer.h new file mode 100644 index 0000000000..7f2f8edea8 --- /dev/null +++ b/guilib/GUIWrappingListContainer.h @@ -0,0 +1,62 @@ +/*! +\file GUIListContainer.h +\brief +*/ + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIBaseContainer.h" +/*! + \ingroup controls + \brief + */ +class CGUIWrappingListContainer : public CGUIBaseContainer +{ +public: + CGUIWrappingListContainer(int parentID, int controlID, float posX, float posY, float width, float height, ORIENTATION orientation, int scrollTime, int preloadItems, int fixedPosition); + virtual ~CGUIWrappingListContainer(void); + virtual CGUIWrappingListContainer *Clone() const { return new CGUIWrappingListContainer(*this); }; + + virtual bool OnAction(const CAction &action); + virtual bool OnMessage(CGUIMessage& message); + virtual int GetSelectedItem() const; + +protected: + virtual void Scroll(int amount); + virtual bool MoveDown(bool wrapAround); + virtual bool MoveUp(bool wrapAround); + virtual void ValidateOffset(); + virtual int CorrectOffset(int offset, int cursor) const; + virtual bool SelectItemFromPoint(const CPoint &point); + virtual void SelectItem(int item); + virtual void Reset(); + virtual unsigned int GetNumItems() const { return m_items.size() - m_extraItems; }; + virtual int GetCurrentPage() const; + virtual void SetPageControlRange(); + virtual void UpdatePageControl(int offset); + + void ResetExtraItems(); + unsigned int m_extraItems; +}; + diff --git a/guilib/Geometry.h b/guilib/Geometry.h new file mode 100644 index 0000000000..2de87d2d64 --- /dev/null +++ b/guilib/Geometry.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +#ifdef __GNUC__ +// under gcc, inline will only take place if optimizations are applied (-O). this will force inline even whith optimizations. +#define XBMC_FORCE_INLINE __attribute__((always_inline)) +#else +#define XBMC_FORCE_INLINE +#endif + + +class CPoint +{ +public: + CPoint() + { + x = 0; y = 0; + }; + + CPoint(float a, float b) + { + x = a; + y = b; + }; + + CPoint operator+(const CPoint &point) const + { + CPoint ans; + ans.x = x + point.x; + ans.y = y + point.y; + return ans; + }; + + const CPoint &operator+=(const CPoint &point) + { + x += point.x; + y += point.y; + return *this; + }; + + CPoint operator-(const CPoint &point) const + { + CPoint ans; + ans.x = x - point.x; + ans.y = y - point.y; + return ans; + }; + + const CPoint &operator-=(const CPoint &point) + { + x -= point.x; + y -= point.y; + return *this; + }; + + float x, y; +}; + +class CRect +{ +public: + CRect() { x1 = y1 = x2 = y2 = 0;}; + CRect(float left, float top, float right, float bottom) { x1 = left; y1 = top; x2 = right; y2 = bottom; }; + + void SetRect(float left, float top, float right, float bottom) { x1 = left; y1 = top; x2 = right; y2 = bottom; }; + + bool PtInRect(const CPoint &point) const + { + if (x1 <= point.x && point.x <= x2 && y1 <= point.y && point.y <= y2) + return true; + return false; + }; + + inline const CRect &operator -=(const CPoint &point) XBMC_FORCE_INLINE + { + x1 -= point.x; + y1 -= point.y; + x2 -= point.x; + y2 -= point.y; + return *this; + }; + + inline const CRect &operator +=(const CPoint &point) XBMC_FORCE_INLINE + { + x1 += point.x; + y1 += point.y; + x2 += point.x; + y2 += point.y; + return *this; + }; + + const CRect &Intersect(const CRect &rect) + { + if (rect.x2 < x2) x2 = rect.x2; + if (rect.y2 < y2) y2 = rect.y2; + if (rect.x1 > x1) x1 = rect.x1; + if (rect.y1 > y1) y1 = rect.y1; + if (x1 > x2) x1 = x2; + if (y1 > y2) y1 = y2; + return *this; + }; + + inline bool IsEmpty() const XBMC_FORCE_INLINE + { + return (x2 - x1) * (y2 - y1) == 0; + }; + + inline float Width() const XBMC_FORCE_INLINE + { + return x2 - x1; + }; + + inline float Height() const XBMC_FORCE_INLINE + { + return y2 - y1; + }; + + bool operator !=(const CRect &rect) const + { + if (x1 != rect.x1) return true; + if (x2 != rect.x2) return true; + if (y1 != rect.y1) return true; + if (y2 != rect.y2) return true; + return false; + }; + + float x1, y1, x2, y2; +}; + diff --git a/guilib/GraphicContext.cpp b/guilib/GraphicContext.cpp new file mode 100644 index 0000000000..5c094a6562 --- /dev/null +++ b/guilib/GraphicContext.cpp @@ -0,0 +1,797 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" +#include "GraphicContext.h" +#include "GUIFontManager.h" +#include "GUIMessage.h" +#include "IMsgSenderCallback.h" +#include "utils/SingleLock.h" +#include "Application.h" +#include "GUISettings.h" +#include "Settings.h" +#include "AdvancedSettings.h" +#include "cores/VideoRenderers/RenderManager.h" +#include "WindowingFactory.h" +#include "SkinInfo.h" +#include "TextureManager.h" +#include "MouseStat.h" + +using namespace std; + +CGraphicContext g_graphicsContext; +extern bool g_fullScreen; + +/* quick access to a skin setting, fine unless we starts clearing video settings */ +static CSettingInt* g_guiSkinzoom = NULL; + +CGraphicContext::CGraphicContext(void) +{ + m_iScreenWidth = 720; + m_iScreenHeight = 576; + m_iScreenId = 0; + m_strMediaDir = ""; + m_bCalibrating = false; + m_Resolution = RES_INVALID; + m_pCallback = NULL; + m_guiScaleX = m_guiScaleY = 1.0f; + m_windowResolution = RES_INVALID; + m_bFullScreenRoot = false; +} + +CGraphicContext::~CGraphicContext(void) +{ +} + +bool CGraphicContext::SendMessage(int message, int senderID, int destID, int param1, int param2) +{ + if (!m_pCallback) return false; + CGUIMessage msg(message, senderID, destID, param1, param2); + return m_pCallback->SendMessage(msg); +} + +bool CGraphicContext::SendMessage(CGUIMessage& message) +{ + if (!m_pCallback) return false; + return m_pCallback->SendMessage(message); +} + +void CGraphicContext::setMessageSender(IMsgSenderCallback* pCallback) +{ + m_pCallback = pCallback; +} + +void CGraphicContext::SetOrigin(float x, float y) +{ + if (m_origins.size()) + m_origins.push(CPoint(x,y) + m_origins.top()); + else + m_origins.push(CPoint(x,y)); + + AddTransform(TransformMatrix::CreateTranslation(x, y)); +} + +void CGraphicContext::RestoreOrigin() +{ + m_origins.pop(); + RemoveTransform(); +} + +// add a new clip region, intersecting with the previous clip region. +bool CGraphicContext::SetClipRegion(float x, float y, float w, float h) +{ // transform from our origin + CPoint origin; + if (m_origins.size()) + origin = m_origins.top(); + + // ok, now intersect with our old clip region + CRect rect(x, y, x + w, y + h); + rect += origin; + if (m_clipRegions.size()) + { + // intersect with original clip region + rect.Intersect(m_clipRegions.top()); + } + + if (rect.IsEmpty()) + return false; + + m_clipRegions.push(rect); + + // here we could set the hardware clipping, if applicable + return true; +} + +void CGraphicContext::RestoreClipRegion() +{ + if (m_clipRegions.size()) + m_clipRegions.pop(); + + // here we could reset the hardware clipping, if applicable +} + +void CGraphicContext::ClipRect(CRect &vertex, CRect &texture, CRect *texture2) +{ + // this is the software clipping routine. If the graphics hardware is set to do the clipping + // (eg via SetClipPlane in D3D for instance) then this routine is unneeded. + if (m_clipRegions.size()) + { + // take a copy of the vertex rectangle and intersect + // it with our clip region (moved to the same coordinate system) + CRect clipRegion(m_clipRegions.top()); + if (m_origins.size()) + clipRegion -= m_origins.top(); + CRect original(vertex); + vertex.Intersect(clipRegion); + // and use the original to compute the texture coordinates + if (original != vertex) + { + float scaleX = texture.Width() / original.Width(); + float scaleY = texture.Height() / original.Height(); + texture.x1 += (vertex.x1 - original.x1) * scaleX; + texture.y1 += (vertex.y1 - original.y1) * scaleY; + texture.x2 += (vertex.x2 - original.x2) * scaleX; + texture.y2 += (vertex.y2 - original.y2) * scaleY; + if (texture2) + { + scaleX = texture2->Width() / original.Width(); + scaleY = texture2->Height() / original.Height(); + texture2->x1 += (vertex.x1 - original.x1) * scaleX; + texture2->y1 += (vertex.y1 - original.y1) * scaleY; + texture2->x2 += (vertex.x2 - original.x2) * scaleX; + texture2->y2 += (vertex.y2 - original.y2) * scaleY; + } + } + } +} + +bool CGraphicContext::SetViewPort(float fx, float fy, float fwidth, float fheight, bool intersectPrevious /* = false */) +{ + CRect oldviewport; + g_Windowing.GetViewPort(oldviewport); + + // transform coordinates - we may have a rotation which changes the positioning of the + // minimal and maximal viewport extents. We currently go to the maximal extent. + float x[4], y[4]; + x[0] = x[3] = fx; + x[1] = x[2] = fx + fwidth; + y[0] = y[1] = fy; + y[2] = y[3] = fy + fheight; + float minX = (float)m_iScreenWidth; + float maxX = 0; + float minY = (float)m_iScreenHeight; + float maxY = 0; + for (int i = 0; i < 4; i++) + { + float z = 0; + ScaleFinalCoords(x[i], y[i], z); + if (x[i] < minX) minX = x[i]; + if (x[i] > maxX) maxX = x[i]; + if (y[i] < minY) minY = y[i]; + if (y[i] > maxY) maxY = y[i]; + } + + int newLeft = (int)(minX + 0.5f); + int newTop = (int)(minY + 0.5f); + int newRight = (int)(maxX + 0.5f); + int newBottom = (int)(maxY + 0.5f); + if (intersectPrevious) + { + // do the intersection + int oldLeft = (int)oldviewport.x1; + int oldTop = (int)oldviewport.y1; + int oldRight = (int)oldviewport.x2; + int oldBottom = (int)oldviewport.y2; + if (newLeft >= oldRight || newTop >= oldBottom || newRight <= oldLeft || newBottom <= oldTop) + { // empty intersection - return false to indicate no rendering should occur + return false; + } + // ok, they intersect, do the intersection + if (newLeft < oldLeft) newLeft = oldLeft; + if (newTop < oldTop) newTop = oldTop; + if (newRight > oldRight) newRight = oldRight; + if (newBottom > oldBottom) newBottom = oldBottom; + } + // check range against screen size + if (newRight <= 0 || newBottom <= 0 || + newTop >= m_iScreenHeight || newLeft >= m_iScreenWidth || + newLeft >= newRight || newTop >= newBottom) + { // no intersection with the screen + return false; + } + // intersection with the screen + if (newLeft < 0) newLeft = 0; + if (newTop < 0) newTop = 0; + if (newRight > m_iScreenWidth) newRight = m_iScreenWidth; + if (newBottom > m_iScreenHeight) newBottom = m_iScreenHeight; + + ASSERT(newLeft < newRight); + ASSERT(newTop < newBottom); + + CRect newviewport((float)newLeft, (float)newTop, (float)newRight, (float)newBottom); + g_Windowing.SetViewPort(newviewport); + + m_viewStack.push(oldviewport); + + UpdateCameraPosition(m_cameras.top()); + return true; +} + +void CGraphicContext::RestoreViewPort() +{ + if (!m_viewStack.size()) return; + + CRect oldviewport = m_viewStack.top(); + g_Windowing.SetViewPort(oldviewport); + + m_viewStack.pop(); + + UpdateCameraPosition(m_cameras.top()); +} + +const RECT& CGraphicContext::GetViewWindow() const +{ + return m_videoRect; +} + +void CGraphicContext::SetViewWindow(float left, float top, float right, float bottom) +{ + if (m_bCalibrating) + { + SetFullScreenViewWindow(m_Resolution); + } + else + { + m_videoRect.left = (long)(ScaleFinalXCoord(left, top) + 0.5f); + m_videoRect.top = (long)(ScaleFinalYCoord(left, top) + 0.5f); + m_videoRect.right = (long)(ScaleFinalXCoord(right, bottom) + 0.5f); + m_videoRect.bottom = (long)(ScaleFinalYCoord(right, bottom) + 0.5f); + } +} + +void CGraphicContext::SetFullScreenViewWindow(RESOLUTION &res) +{ + m_videoRect.left = g_settings.m_ResInfo[res].Overscan.left; + m_videoRect.top = g_settings.m_ResInfo[res].Overscan.top; + m_videoRect.right = g_settings.m_ResInfo[res].Overscan.right; + m_videoRect.bottom = g_settings.m_ResInfo[res].Overscan.bottom; +} + +void CGraphicContext::SetFullScreenVideo(bool bOnOff) +{ + Lock(); + m_bFullScreenVideo = bOnOff; + +#if defined(HAS_VIDEO_PLAYBACK) + if(m_bFullScreenRoot) + { + if(m_bFullScreenVideo) + g_graphicsContext.SetVideoResolution(g_renderManager.GetResolution()); + else if(g_guiSettings.m_LookAndFeelResolution > RES_DESKTOP) + g_graphicsContext.SetVideoResolution(g_guiSettings.m_LookAndFeelResolution); + else + g_graphicsContext.SetVideoResolution(RES_DESKTOP); + } + else + g_graphicsContext.SetVideoResolution(RES_WINDOW); +#endif + + SetFullScreenViewWindow(m_Resolution); + Unlock(); +} + +bool CGraphicContext::IsFullScreenVideo() const +{ + return m_bFullScreenVideo; +} + +bool CGraphicContext::IsCalibrating() const +{ + return m_bCalibrating; +} + +void CGraphicContext::SetCalibrating(bool bOnOff) +{ + m_bCalibrating = bOnOff; +} + +bool CGraphicContext::IsValidResolution(RESOLUTION res) +{ + if (res >= RES_WINDOW && (size_t) res <= g_settings.m_ResInfo.size()) + { + return true; + } + + return false; +} + +void CGraphicContext::SetVideoResolution(RESOLUTION res, bool forceUpdate) +{ + RESOLUTION lastRes = m_Resolution; + + // If the user asked us to guess, go with desktop + if (res == RES_AUTORES || !IsValidResolution(res)) + { + res = RES_DESKTOP; + } + + // If we are switching to the same resolution and same window/full-screen, no need to do anything + if (!forceUpdate && res == lastRes && m_bFullScreenRoot == g_advancedSettings.m_fullScreen) + { + return; + } + + if (res >= RES_DESKTOP || g_advancedSettings.m_startFullScreen) + { + g_advancedSettings.m_fullScreen = true; + m_bFullScreenRoot = true; + } + else + { + g_advancedSettings.m_fullScreen = false; + m_bFullScreenRoot = false; + } + + Lock(); + + m_iScreenWidth = g_settings.m_ResInfo[res].iWidth; + m_iScreenHeight = g_settings.m_ResInfo[res].iHeight; + m_iScreenId = g_settings.m_ResInfo[res].iScreen; + m_Resolution = res; + + if (g_advancedSettings.m_fullScreen) + { + bool blankOtherDisplays = g_guiSettings.GetInt("videoscreen.displayblanking") == BLANKING_ALL_DISPLAYS; + g_Windowing.SetFullScreen(true, g_settings.m_ResInfo[res], blankOtherDisplays); + } + else if (lastRes >= RES_DESKTOP ) + g_Windowing.SetFullScreen(false, g_settings.m_ResInfo[res], false); + else + g_Windowing.ResizeWindow(m_iScreenWidth, m_iScreenHeight, -1, -1); + + // set the mouse resolution + g_renderManager.Recover(); + g_Mouse.SetResolution(m_iScreenWidth, m_iScreenHeight, 1, 1); + g_fontManager.ReloadTTFFonts(); + + SetFullScreenViewWindow(res); + + Unlock(); +} + +RESOLUTION CGraphicContext::GetVideoResolution() const +{ + return m_Resolution; +} + +void CGraphicContext::ResetOverscan(RESOLUTION_INFO &res) +{ + res.Overscan.left = 0; + res.Overscan.top = 0; + res.Overscan.right = res.iWidth; + res.Overscan.bottom = res.iHeight; +} + +void CGraphicContext::ResetOverscan(RESOLUTION res, OVERSCAN &overscan) +{ + overscan.left = 0; + overscan.top = 0; + switch (res) + { + case RES_HDTV_1080i: + overscan.right = 1920; + overscan.bottom = 1080; + break; + case RES_HDTV_720p: + overscan.right = 1280; + overscan.bottom = 720; + break; + case RES_HDTV_480p_16x9: + case RES_HDTV_480p_4x3: + case RES_NTSC_16x9: + case RES_NTSC_4x3: + case RES_PAL60_16x9: + case RES_PAL60_4x3: + overscan.right = 720; + overscan.bottom = 480; + break; + case RES_PAL_16x9: + case RES_PAL_4x3: + overscan.right = 720; + overscan.bottom = 576; + break; + default: + overscan.right = g_settings.m_ResInfo[res].iWidth; + overscan.bottom = g_settings.m_ResInfo[res].iHeight; + break; + } +} + +void CGraphicContext::ResetScreenParameters(RESOLUTION res) +{ + // For now these are all on the first screen. + g_settings.m_ResInfo[res].iScreen = 0; + + // 1080i + switch (res) + { + case RES_HDTV_1080i: + g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 1080); + g_settings.m_ResInfo[res].iWidth = 1920; + g_settings.m_ResInfo[res].iHeight = 1080; + g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED | D3DPRESENTFLAG_WIDESCREEN; + g_settings.m_ResInfo[res].fPixelRatio = 1.0f; + g_settings.m_ResInfo[res].strMode ="1080i 16:9"; + break; + case RES_HDTV_720p: + g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 720); + g_settings.m_ResInfo[res].iWidth = 1280; + g_settings.m_ResInfo[res].iHeight = 720; + g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_PROGRESSIVE | D3DPRESENTFLAG_WIDESCREEN; + g_settings.m_ResInfo[res].fPixelRatio = 1.0f; + g_settings.m_ResInfo[res].strMode = "720p 16:9"; + break; + case RES_HDTV_480p_4x3: + g_settings.m_ResInfo[res].iSubtitles = (int)(0.9 * 480); + g_settings.m_ResInfo[res].iWidth = 720; + g_settings.m_ResInfo[res].iHeight = 480; + g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_PROGRESSIVE; + g_settings.m_ResInfo[res].fPixelRatio = 4320.0f / 4739.0f; + g_settings.m_ResInfo[res].strMode = "480p 4:3"; + break; + case RES_HDTV_480p_16x9: + g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 480); + g_settings.m_ResInfo[res].iWidth = 720; + g_settings.m_ResInfo[res].iHeight = 480; + g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_PROGRESSIVE | D3DPRESENTFLAG_WIDESCREEN; + g_settings.m_ResInfo[res].fPixelRatio = 4320.0f / 4739.0f*4.0f / 3.0f; + g_settings.m_ResInfo[res].strMode = "480p 16:9"; + break; + case RES_NTSC_4x3: + g_settings.m_ResInfo[res].iSubtitles = (int)(0.9 * 480); + g_settings.m_ResInfo[res].iWidth = 720; + g_settings.m_ResInfo[res].iHeight = 480; + g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED; + g_settings.m_ResInfo[res].fPixelRatio = 4320.0f / 4739.0f; + g_settings.m_ResInfo[res].strMode = "NTSC 4:3"; + break; + case RES_NTSC_16x9: + g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 480); + g_settings.m_ResInfo[res].iWidth = 720; + g_settings.m_ResInfo[res].iHeight = 480; + g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED | D3DPRESENTFLAG_WIDESCREEN; + g_settings.m_ResInfo[res].fPixelRatio = 4320.0f / 4739.0f*4.0f / 3.0f; + g_settings.m_ResInfo[res].strMode = "NTSC 16:9"; + break; + case RES_PAL_4x3: + g_settings.m_ResInfo[res].iSubtitles = (int)(0.9 * 576); + g_settings.m_ResInfo[res].iWidth = 720; + g_settings.m_ResInfo[res].iHeight = 576; + g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED; + g_settings.m_ResInfo[res].fPixelRatio = 128.0f / 117.0f; + g_settings.m_ResInfo[res].strMode = "PAL 4:3"; + break; + case RES_PAL_16x9: + g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 576); + g_settings.m_ResInfo[res].iWidth = 720; + g_settings.m_ResInfo[res].iHeight = 576; + g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED | D3DPRESENTFLAG_WIDESCREEN; + g_settings.m_ResInfo[res].fPixelRatio = 128.0f / 117.0f*4.0f / 3.0f; + g_settings.m_ResInfo[res].strMode = "PAL 16:9"; + break; + case RES_PAL60_4x3: + g_settings.m_ResInfo[res].iSubtitles = (int)(0.9 * 480); + g_settings.m_ResInfo[res].iWidth = 720; + g_settings.m_ResInfo[res].iHeight = 480; + g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED; + g_settings.m_ResInfo[res].fPixelRatio = 4320.0f / 4739.0f; + g_settings.m_ResInfo[res].strMode = "PAL60 4:3"; + break; + case RES_PAL60_16x9: + g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 480); + g_settings.m_ResInfo[res].iWidth = 720; + g_settings.m_ResInfo[res].iHeight = 480; + g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED | D3DPRESENTFLAG_WIDESCREEN; + g_settings.m_ResInfo[res].fPixelRatio = 4320.0f / 4739.0f*4.0f / 3.0f; + g_settings.m_ResInfo[res].strMode = "PAL60 16:9"; + break; + default: + break; + } + ResetOverscan(res, g_settings.m_ResInfo[res].Overscan); +} + +float CGraphicContext::GetPixelRatio(RESOLUTION iRes) const +{ + if (iRes >= 0 && iRes < (int)g_settings.m_ResInfo.size()) + return g_settings.m_ResInfo[iRes].fPixelRatio; + return 0.0f; +} + +void CGraphicContext::Clear() +{ + g_Windowing.ClearBuffers(0, 0, 0, 0); +} + +void CGraphicContext::CaptureStateBlock() +{ + g_Windowing.CaptureStateBlock(); +} + +void CGraphicContext::ApplyStateBlock() +{ + g_Windowing.ApplyStateBlock(); +} + +void CGraphicContext::SetScalingResolution(RESOLUTION res, float posX, float posY, bool needsScaling) +{ + Lock(); + m_windowResolution = res; + if (needsScaling && m_Resolution != RES_INVALID) + { + // calculate necessary scalings + float fFromWidth; + float fFromHeight; + float fToPosX; + float fToPosY; + float fToWidth; + float fToHeight; + + { + fFromWidth = (float)g_settings.m_ResInfo[res].iWidth; + fFromHeight = (float)g_settings.m_ResInfo[res].iHeight; + fToPosX = (float)g_settings.m_ResInfo[m_Resolution].Overscan.left; + fToPosY = (float)g_settings.m_ResInfo[m_Resolution].Overscan.top; + fToWidth = (float)g_settings.m_ResInfo[m_Resolution].Overscan.right - fToPosX; + fToHeight = (float)g_settings.m_ResInfo[m_Resolution].Overscan.bottom - fToPosY; + } + + // add additional zoom to compensate for any overskan built in skin + float fZoom = g_SkinInfo.GetSkinZoom(); + + if(!g_guiSkinzoom) // lookup gui setting if we didn't have it already + g_guiSkinzoom = (CSettingInt*)g_guiSettings.GetSetting("lookandfeel.skinzoom"); + + if(g_guiSkinzoom) + fZoom *= (100 + g_guiSkinzoom->GetData()) * 0.01f; + + fZoom -= 1.0f; + fToPosX -= fToWidth * fZoom * 0.5f; + fToWidth *= fZoom + 1.0f; + + // adjust for aspect ratio as zoom is given in the vertical direction and we don't + // do aspect ratio corrections in the gui code + fZoom = fZoom / g_settings.m_ResInfo[m_Resolution].fPixelRatio; + fToPosY -= fToHeight * fZoom * 0.5f; + fToHeight *= fZoom + 1.0f; + + m_guiScaleX = fFromWidth / fToWidth; + m_guiScaleY = fFromHeight / fToHeight; + TransformMatrix windowOffset = TransformMatrix::CreateTranslation(posX, posY); + TransformMatrix guiScaler = TransformMatrix::CreateScaler(fToWidth / fFromWidth, fToHeight / fFromHeight, fToHeight / fFromHeight); + TransformMatrix guiOffset = TransformMatrix::CreateTranslation(fToPosX, fToPosY); + m_guiTransform = guiOffset * guiScaler * windowOffset; + } + else + { + m_guiTransform = TransformMatrix::CreateTranslation(posX, posY); + m_guiScaleX = 1.0f; + m_guiScaleY = 1.0f; + } + // reset our origin and camera + while (m_origins.size()) + m_origins.pop(); + m_origins.push(CPoint(posX, posY)); + while (m_cameras.size()) + m_cameras.pop(); + m_cameras.push(CPoint(0.5f*m_iScreenWidth, 0.5f*m_iScreenHeight)); + + // and reset the final transform + UpdateFinalTransform(m_guiTransform); + Unlock(); +} + +void CGraphicContext::SetRenderingResolution(RESOLUTION res, float posX, float posY, bool needsScaling) +{ + Lock(); + SetScalingResolution(res, posX, posY, needsScaling); + UpdateCameraPosition(m_cameras.top()); + Unlock(); +} + +void CGraphicContext::UpdateFinalTransform(const TransformMatrix &matrix) +{ + m_finalTransform = matrix; + // We could set the world transform here to GPU-ize the animation system. + // trouble is that we require the resulting x,y coords to be rounded to + // the nearest pixel (vertex shader perhaps?) +} + +void CGraphicContext::InvertFinalCoords(float &x, float &y) const +{ + m_finalTransform.InverseTransformPosition(x, y); +} + +float CGraphicContext::GetScalingPixelRatio() const +{ + if (m_Resolution == m_windowResolution) + return GetPixelRatio(m_windowResolution); + + RESOLUTION checkRes = m_windowResolution; + if (checkRes == RES_INVALID) + checkRes = m_Resolution; + // resolutions are different - we want to return the aspect ratio of the video resolution + // but only once it's been corrected for the skin -> screen coordinates scaling + float winWidth = (float)g_settings.m_ResInfo[checkRes].iWidth; + float winHeight = (float)g_settings.m_ResInfo[checkRes].iHeight; + float outWidth = (float)g_settings.m_ResInfo[m_Resolution].iWidth; + float outHeight = (float)g_settings.m_ResInfo[m_Resolution].iHeight; + float outPR = GetPixelRatio(m_Resolution); + + return outPR * (outWidth / outHeight) / (winWidth / winHeight); +} + +void CGraphicContext::SetCameraPosition(const CPoint &camera) +{ + // offset the camera from our current location (this is in XML coordinates) and scale it up to + // the screen resolution + CPoint cam(camera); + if (m_origins.size()) + cam += m_origins.top(); + + RESOLUTION windowRes = (m_windowResolution == RES_INVALID) ? m_Resolution : m_windowResolution; + cam.x *= (float)m_iScreenWidth / g_settings.m_ResInfo[windowRes].iWidth; + cam.y *= (float)m_iScreenHeight / g_settings.m_ResInfo[windowRes].iHeight; + + m_cameras.push(cam); + UpdateCameraPosition(m_cameras.top()); +} + +void CGraphicContext::RestoreCameraPosition() +{ // remove the top camera from the stack + ASSERT(m_cameras.size()); + m_cameras.pop(); + UpdateCameraPosition(m_cameras.top()); +} + +// NOTE: This routine is currently called (twice) every time there is a <camera> +// tag in the skin. It actually only has to be called before we render +// something, so another option is to just save the camera coordinates +// and then have a routine called before every draw that checks whether +// the camera has changed, and if so, changes it. Similarly, it could set +// the world transform at that point as well (or even combine world + view +// to cut down on one setting) +void CGraphicContext::UpdateCameraPosition(const CPoint &camera) +{ + g_Windowing.SetCameraPosition(camera, m_iScreenWidth, m_iScreenHeight); +} + +bool CGraphicContext::RectIsAngled(float x1, float y1, float x2, float y2) const +{ // need only test 3 points, as they must be co-planer + if (m_finalTransform.TransformZCoord(x1, y1, 0)) return true; + if (m_finalTransform.TransformZCoord(x2, y2, 0)) return true; + if (m_finalTransform.TransformZCoord(x1, y2, 0)) return true; + return false; +} + +float CGraphicContext::GetFPS() const +{ + if (m_Resolution != RES_INVALID) + { + if (g_settings.m_ResInfo[m_Resolution].fRefreshRate > 0) + return g_settings.m_ResInfo[m_Resolution].fRefreshRate; + if (m_Resolution == RES_PAL_4x3 || m_Resolution == RES_PAL_16x9) + return 50.0f; + if (m_Resolution == RES_HDTV_1080i) + return 30.0f; + } + return 60.0f; +} + +void CGraphicContext::BeginPaint(bool lock) +{ + if (lock) Lock(); +} + +void CGraphicContext::EndPaint(bool lock) +{ + if (lock) Unlock(); +} + +bool CGraphicContext::IsFullScreenRoot () const +{ + return m_bFullScreenRoot; +} + +bool CGraphicContext::ToggleFullScreenRoot () +{ + RESOLUTION newRes; + + if (m_bFullScreenRoot) + { + newRes = RES_WINDOW; + } + else + { + if (g_guiSettings.m_LookAndFeelResolution > RES_DESKTOP) + newRes = g_guiSettings.m_LookAndFeelResolution; + else + newRes = RES_DESKTOP; + +#if defined(HAS_VIDEO_PLAYBACK) + if (g_graphicsContext.IsFullScreenVideo() || g_graphicsContext.IsCalibrating()) + { + /* we need to trick renderer that we are fullscreen already so it gives us a valid value */ + m_bFullScreenRoot = true; + newRes = g_renderManager.GetResolution(); + m_bFullScreenRoot = false; + } +#endif + + } + + SetVideoResolution(newRes); + + return m_bFullScreenRoot; +} + +void CGraphicContext::SetMediaDir(const CStdString &strMediaDir) +{ + g_TextureManager.SetTexturePath(strMediaDir); + m_strMediaDir = strMediaDir; +} + +void CGraphicContext::Flip() +{ + g_Windowing.PresentRender(); +} + +void CGraphicContext::ApplyHardwareTransform() +{ + g_Windowing.ApplyHardwareTransform(m_finalTransform); +} + +void CGraphicContext::RestoreHardwareTransform() +{ + g_Windowing.RestoreHardwareTransform(); +} + +void CGraphicContext::NotifyAppFocusChange(bool bGaining) +{ + g_Windowing.NotifyAppFocusChange(bGaining); +} + +void CGraphicContext::ClipToViewWindow() +{ +} + +void CGraphicContext::GetAllowedResolutions(vector<RESOLUTION> &res) +{ + res.clear(); + + res.push_back(RES_WINDOW); + res.push_back(RES_DESKTOP); + for (size_t r = (size_t) RES_CUSTOM; r < g_settings.m_ResInfo.size(); r++) + { + res.push_back((RESOLUTION) r); + } +} + diff --git a/guilib/GraphicContext.h b/guilib/GraphicContext.h new file mode 100644 index 0000000000..1d2adf03f7 --- /dev/null +++ b/guilib/GraphicContext.h @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +/*! +\file GraphicContext.h +\brief +*/ + +#ifndef GUILIB_GRAPHICCONTEXT_H +#define GUILIB_GRAPHICCONTEXT_H + +#pragma once + +#ifdef __GNUC__ +// under gcc, inline will only take place if optimizations are applied (-O). this will force inline even whith optimizations. +#define XBMC_FORCE_INLINE __attribute__((always_inline)) +#else +#define XBMC_FORCE_INLINE +#endif + + +#include <vector> +#include <stack> +#include <map> +#include "utils/CriticalSection.h" // base class +#include "TransformMatrix.h" // for the members m_guiTransform etc. +#include "Geometry.h" // for CRect/CPoint +#include "gui3d.h" +#include "StdString.h" +#include "Resolution.h" + +// forward definitions +class IMsgSenderCallback; +class CGUIMessage; + +enum VIEW_TYPE { VIEW_TYPE_NONE = 0, + VIEW_TYPE_LIST, + VIEW_TYPE_ICON, + VIEW_TYPE_BIG_LIST, + VIEW_TYPE_BIG_ICON, + VIEW_TYPE_WIDE, + VIEW_TYPE_BIG_WIDE, + VIEW_TYPE_WRAP, + VIEW_TYPE_BIG_WRAP, + VIEW_TYPE_AUTO, + VIEW_TYPE_MAX }; + + +class CGraphicContext : public CCriticalSection +{ +public: + CGraphicContext(void); + virtual ~CGraphicContext(void); + + // the following two functions should wrap any + // GL calls to maintain thread safety + void BeginPaint(bool lock=true); + void EndPaint(bool lock=true); + + int GetWidth() const { return m_iScreenWidth; } + int GetHeight() const { return m_iScreenHeight; } + float GetFPS() const; + bool SendMessage(CGUIMessage& message); + bool SendMessage(int message, int senderID, int destID, int param1 = 0, int param2 = 0); + void setMessageSender(IMsgSenderCallback* pCallback); + const CStdString& GetMediaDir() const { return m_strMediaDir; } + void SetMediaDir(const CStdString& strMediaDir); + bool IsWidescreen() const { return m_bWidescreen; } + bool SetViewPort(float fx, float fy , float fwidth, float fheight, bool intersectPrevious = false); + void RestoreViewPort(); + const RECT& GetViewWindow() const; + void SetViewWindow(float left, float top, float right, float bottom); + void SetFullScreenViewWindow(RESOLUTION &res); + bool IsFullScreenRoot() const; + bool ToggleFullScreenRoot(); + void SetFullScreenVideo(bool bOnOff); + bool IsFullScreenVideo() const; + bool IsCalibrating() const; + void SetCalibrating(bool bOnOff); + bool IsValidResolution(RESOLUTION res); + void SetVideoResolution(RESOLUTION res, bool forceUpdate = false); + RESOLUTION GetVideoResolution() const; + void ResetOverscan(RESOLUTION res, OVERSCAN &overscan); + void ResetOverscan(RESOLUTION_INFO &resinfo); + void ResetScreenParameters(RESOLUTION res); + void Lock() { EnterCriticalSection(*this); } + void Unlock() { LeaveCriticalSection(*this); } + float GetPixelRatio(RESOLUTION iRes) const; + void CaptureStateBlock(); + void ApplyStateBlock(); + void Clear(); + void GetAllowedResolutions(std::vector<RESOLUTION> &res); + + // output scaling + void SetRenderingResolution(RESOLUTION res, float posX, float posY, bool needsScaling); ///< Sets scaling up for rendering + void SetScalingResolution(RESOLUTION res, float posX, float posY, bool needsScaling); ///< Sets scaling up for skin loading etc. + float GetScalingPixelRatio() const; + void Flip(); + void InvertFinalCoords(float &x, float &y) const; + inline float ScaleFinalXCoord(float x, float y) const XBMC_FORCE_INLINE { return m_finalTransform.TransformXCoord(x, y, 0); } + inline float ScaleFinalYCoord(float x, float y) const XBMC_FORCE_INLINE { return m_finalTransform.TransformYCoord(x, y, 0); } + inline float ScaleFinalZCoord(float x, float y) const XBMC_FORCE_INLINE { return m_finalTransform.TransformZCoord(x, y, 0); } + inline void ScaleFinalCoords(float &x, float &y, float &z) const XBMC_FORCE_INLINE { m_finalTransform.TransformPosition(x, y, z); } + bool RectIsAngled(float x1, float y1, float x2, float y2) const; + + inline float GetGUIScaleX() const XBMC_FORCE_INLINE { return m_guiScaleX; } + inline float GetGUIScaleY() const XBMC_FORCE_INLINE { return m_guiScaleY; } + inline color_t MergeAlpha(color_t color) const XBMC_FORCE_INLINE + { + color_t alpha = m_finalTransform.TransformAlpha((color >> 24) & 0xff); + if (alpha > 255) alpha = 255; + return ((alpha << 24) & 0xff000000) | (color & 0xffffff); + } + + void SetOrigin(float x, float y); + void RestoreOrigin(); + void SetCameraPosition(const CPoint &camera); + void RestoreCameraPosition(); + bool SetClipRegion(float x, float y, float w, float h); + void RestoreClipRegion(); + void ApplyHardwareTransform(); + void RestoreHardwareTransform(); + void NotifyAppFocusChange(bool bGaining); + void ClipRect(CRect &vertex, CRect &texture, CRect *diffuse = NULL); + void ClipToViewWindow(); + inline void ResetWindowTransform() + { + while (m_groupTransform.size()) + m_groupTransform.pop(); + m_groupTransform.push(m_guiTransform); + } + inline void AddTransform(const TransformMatrix &matrix) + { + ASSERT(m_groupTransform.size()); + if (m_groupTransform.size()) + m_groupTransform.push(m_groupTransform.top() * matrix); + else + m_groupTransform.push(matrix); + UpdateFinalTransform(m_groupTransform.top()); + } + inline void RemoveTransform() + { + ASSERT(m_groupTransform.size() > 1); + if (m_groupTransform.size()) + m_groupTransform.pop(); + if (m_groupTransform.size()) + UpdateFinalTransform(m_groupTransform.top()); + else + UpdateFinalTransform(TransformMatrix()); + } + +protected: + IMsgSenderCallback* m_pCallback; + std::stack<CRect> m_viewStack; + + int m_iScreenHeight; + int m_iScreenWidth; + int m_iScreenId; + int m_iBackBufferCount; + bool m_bWidescreen; + CStdString m_strMediaDir; + RECT m_videoRect; + bool m_bFullScreenRoot; + bool m_bFullScreenVideo; + bool m_bCalibrating; + RESOLUTION m_Resolution; + +private: + void UpdateCameraPosition(const CPoint &camera); + void UpdateFinalTransform(const TransformMatrix &matrix); + RESOLUTION m_windowResolution; + float m_guiScaleX; + float m_guiScaleY; + std::stack<CPoint> m_cameras; + std::stack<CPoint> m_origins; + std::stack<CRect> m_clipRegions; + + TransformMatrix m_guiTransform; + TransformMatrix m_finalTransform; + std::stack<TransformMatrix> m_groupTransform; +}; + +/*! + \ingroup graphics + \brief + */ +extern CGraphicContext g_graphicsContext; +#endif diff --git a/guilib/IAudioDeviceChangedCallback.h b/guilib/IAudioDeviceChangedCallback.h new file mode 100644 index 0000000000..c9d55b7a23 --- /dev/null +++ b/guilib/IAudioDeviceChangedCallback.h @@ -0,0 +1,31 @@ +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +class IAudioDeviceChangedCallback +{ +public: + virtual void Initialize(int iDevice)=0; + virtual void DeInitialize(int iDevice)=0; + virtual ~IAudioDeviceChangedCallback() {} +}; + diff --git a/guilib/IMsgSenderCallback.h b/guilib/IMsgSenderCallback.h new file mode 100644 index 0000000000..3f825d4469 --- /dev/null +++ b/guilib/IMsgSenderCallback.h @@ -0,0 +1,45 @@ +/*! +\file IMsgSenderCallback.h +\brief +*/ + +#ifndef GUILIB_IMSGSENDERCALLBACK +#define GUILIB_IMSGSENDERCALLBACK + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIMessage.h" + +/*! + \ingroup winman + \brief + */ +class IMsgSenderCallback +{ +public: + virtual bool SendMessage(CGUIMessage& message) = 0; + virtual ~IMsgSenderCallback() {} +}; + +#endif diff --git a/guilib/IMsgTargetCallback.h b/guilib/IMsgTargetCallback.h new file mode 100644 index 0000000000..7af60fded2 --- /dev/null +++ b/guilib/IMsgTargetCallback.h @@ -0,0 +1,45 @@ +/*! +\file IMsgTargetCallback.h +\brief +*/ + +#ifndef GUILIB_IMSGTARGETCALLBACK +#define GUILIB_IMSGTARGETCALLBACK + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIMessage.h" + +/*! + \ingroup winman + \brief + */ +class IMsgTargetCallback +{ +public: + virtual bool OnMessage(CGUIMessage& message) = 0; + virtual ~IMsgTargetCallback() {} +}; + +#endif diff --git a/guilib/IWindowManagerCallback.cpp b/guilib/IWindowManagerCallback.cpp new file mode 100644 index 0000000000..a47634c498 --- /dev/null +++ b/guilib/IWindowManagerCallback.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "IWindowManagerCallback.h" + + +IWindowManagerCallback::IWindowManagerCallback(void) +{} + +IWindowManagerCallback::~IWindowManagerCallback(void) +{} diff --git a/guilib/IWindowManagerCallback.h b/guilib/IWindowManagerCallback.h new file mode 100644 index 0000000000..25391b228c --- /dev/null +++ b/guilib/IWindowManagerCallback.h @@ -0,0 +1,42 @@ +/*! +\file IWindowManagerCallback.h +\brief +*/ + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +/*! + \ingroup winman + \brief + */ +class IWindowManagerCallback +{ +public: + IWindowManagerCallback(void); + virtual ~IWindowManagerCallback(void); + + virtual void FrameMove() = 0; + virtual void Render() = 0; + virtual void Process() = 0; +}; diff --git a/guilib/Key.cpp b/guilib/Key.cpp new file mode 100644 index 0000000000..7d19a2a47d --- /dev/null +++ b/guilib/Key.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" +#include "Key.h" + +CKey::CKey(void) +{ + m_buttonCode = KEY_INVALID; + m_leftTrigger = 0; + m_rightTrigger = 0; + m_leftThumbX = 0.0f; + m_leftThumbY = 0.0f; + m_rightThumbX = 0.0f; + m_rightThumbY = 0.0f; + m_repeat = 0.0f; + m_fromHttpApi = false; + m_held = 0; +} + +CKey::~CKey(void) +{} + +CKey::CKey(uint32_t buttonCode, uint8_t leftTrigger, uint8_t rightTrigger, float leftThumbX, float leftThumbY, float rightThumbX, float rightThumbY, float repeat) +{ + m_leftTrigger = leftTrigger; + m_rightTrigger = rightTrigger; + m_leftThumbX = leftThumbX; + m_leftThumbY = leftThumbY; + m_rightThumbX = rightThumbX; + m_rightThumbY = rightThumbY; + m_buttonCode = buttonCode; + m_repeat = repeat; + m_fromHttpApi = false; + m_held = 0; +} + +CKey::CKey(const CKey& key) +{ + *this = key; +} + +uint32_t CKey::GetButtonCode() const // for backwards compatibility only +{ + return m_buttonCode; +} + +uint32_t CKey::GetUnicode() const +{ + if (m_buttonCode>=KEY_ASCII && m_buttonCode < KEY_UNICODE) // will need to change when Unicode is fully implemented + return m_buttonCode-KEY_ASCII; + else + return 0; +} + +const CKey& CKey::operator=(const CKey& key) +{ + if (&key == this) return * this; + m_leftTrigger = key.m_leftTrigger; + m_rightTrigger = key.m_rightTrigger; + m_buttonCode = key.m_buttonCode; + m_leftThumbX = key.m_leftThumbX; + m_leftThumbY = key.m_leftThumbY; + m_rightThumbX = key.m_rightThumbX; + m_rightThumbY = key.m_rightThumbY; + m_repeat = key.m_repeat; + m_fromHttpApi = key.m_fromHttpApi; + m_held = key.m_held; + return *this; +} + +BYTE CKey::GetLeftTrigger() const +{ + return m_leftTrigger; +} + +BYTE CKey::GetRightTrigger() const +{ + return m_rightTrigger; +} + +float CKey::GetLeftThumbX() const +{ + return m_leftThumbX; +} + +float CKey::GetLeftThumbY() const +{ + return m_leftThumbY; +} + + +float CKey::GetRightThumbX() const +{ + return m_rightThumbX; +} + +float CKey::GetRightThumbY() const +{ + return m_rightThumbY; +} + +bool CKey::FromKeyboard() const +{ + return (m_buttonCode >= KEY_VKEY && m_buttonCode != KEY_INVALID); +} + +bool CKey::IsAnalogButton() const +{ + if ((GetButtonCode() > 261 && GetButtonCode() < 270) || (GetButtonCode() > 279 && GetButtonCode() < 284)) + return true; + + return false; +} + +bool CKey::IsIRRemote() const +{ + if (GetButtonCode() < 256) + return true; + return false; +} + +float CKey::GetRepeat() const +{ + return m_repeat; +} + +bool CKey::GetFromHttpApi() const +{ + return m_fromHttpApi; +} + +void CKey::SetFromHttpApi(bool bFromHttpApi) +{ + m_fromHttpApi = bFromHttpApi; +} + +void CKey::SetHeld(unsigned int held) +{ + m_held = held; +} + +unsigned int CKey::GetHeld() const +{ + return m_held; +} + diff --git a/guilib/Key.h b/guilib/Key.h new file mode 100644 index 0000000000..d5328443c0 --- /dev/null +++ b/guilib/Key.h @@ -0,0 +1,460 @@ +/*! + \file Key.h + \brief + */ + +#ifndef GUILIB_KEY +#define GUILIB_KEY + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "XBIRRemote.h" +#include <StdString.h> + +// Analogue - don't change order +#define KEY_BUTTON_A 256 +#define KEY_BUTTON_B 257 +#define KEY_BUTTON_X 258 +#define KEY_BUTTON_Y 259 +#define KEY_BUTTON_BLACK 260 +#define KEY_BUTTON_WHITE 261 +#define KEY_BUTTON_LEFT_TRIGGER 262 +#define KEY_BUTTON_RIGHT_TRIGGER 263 + +#define KEY_BUTTON_LEFT_THUMB_STICK 264 +#define KEY_BUTTON_RIGHT_THUMB_STICK 265 + +#define KEY_BUTTON_RIGHT_THUMB_STICK_UP 266 // right thumb stick directions +#define KEY_BUTTON_RIGHT_THUMB_STICK_DOWN 267 // for defining different actions per direction +#define KEY_BUTTON_RIGHT_THUMB_STICK_LEFT 268 +#define KEY_BUTTON_RIGHT_THUMB_STICK_RIGHT 269 + +// Digital - don't change order +#define KEY_BUTTON_DPAD_UP 270 +#define KEY_BUTTON_DPAD_DOWN 271 +#define KEY_BUTTON_DPAD_LEFT 272 +#define KEY_BUTTON_DPAD_RIGHT 273 + +#define KEY_BUTTON_START 274 +#define KEY_BUTTON_BACK 275 + +#define KEY_BUTTON_LEFT_THUMB_BUTTON 276 +#define KEY_BUTTON_RIGHT_THUMB_BUTTON 277 + +#define KEY_BUTTON_LEFT_ANALOG_TRIGGER 278 +#define KEY_BUTTON_RIGHT_ANALOG_TRIGGER 279 + +#define KEY_BUTTON_LEFT_THUMB_STICK_UP 280 // left thumb stick directions +#define KEY_BUTTON_LEFT_THUMB_STICK_DOWN 281 // for defining different actions per direction +#define KEY_BUTTON_LEFT_THUMB_STICK_LEFT 282 +#define KEY_BUTTON_LEFT_THUMB_STICK_RIGHT 283 + +#define KEY_VMOUSE 0xEFFF + +// 0xF000 -> 0xF200 is reserved for the keyboard; a keyboard press is either +#define KEY_VKEY 0xF000 // a virtual key/functional key e.g. cursor left +#define KEY_ASCII 0xF100 // a printable character in the range of TRUE ASCII (from 0 to 127) // FIXME make it clean and pure unicode! remove the need for KEY_ASCII +#define KEY_UNICODE 0xF200 // another printable character whose range is not included in this KEY code + +#define KEY_INVALID 0xFFFF + +// actions that we have defined... +#define ACTION_NONE 0 +#define ACTION_MOVE_LEFT 1 +#define ACTION_MOVE_RIGHT 2 +#define ACTION_MOVE_UP 3 +#define ACTION_MOVE_DOWN 4 +#define ACTION_PAGE_UP 5 +#define ACTION_PAGE_DOWN 6 +#define ACTION_SELECT_ITEM 7 +#define ACTION_HIGHLIGHT_ITEM 8 +#define ACTION_PARENT_DIR 9 +#define ACTION_PREVIOUS_MENU 10 +#define ACTION_SHOW_INFO 11 + +#define ACTION_PAUSE 12 +#define ACTION_STOP 13 +#define ACTION_NEXT_ITEM 14 +#define ACTION_PREV_ITEM 15 +#define ACTION_FORWARD 16 // Can be used to specify specific action in a window, Playback control is handled in ACTION_PLAYER_* +#define ACTION_REWIND 17 // Can be used to specify specific action in a window, Playback control is handled in ACTION_PLAYER_* + +#define ACTION_SHOW_GUI 18 // toggle between GUI and movie or GUI and visualisation. +#define ACTION_ASPECT_RATIO 19 // toggle quick-access zoom modes. Can b used in videoFullScreen.zml window id=2005 +#define ACTION_STEP_FORWARD 20 // seek +1% in the movie. Can b used in videoFullScreen.xml window id=2005 +#define ACTION_STEP_BACK 21 // seek -1% in the movie. Can b used in videoFullScreen.xml window id=2005 +#define ACTION_BIG_STEP_FORWARD 22 // seek +10% in the movie. Can b used in videoFullScreen.xml window id=2005 +#define ACTION_BIG_STEP_BACK 23 // seek -10% in the movie. Can b used in videoFullScreen.xml window id=2005 +#define ACTION_SHOW_OSD 24 // show/hide OSD. Can b used in videoFullScreen.xml window id=2005 +#define ACTION_SHOW_SUBTITLES 25 // turn subtitles on/off. Can b used in videoFullScreen.xml window id=2005 +#define ACTION_NEXT_SUBTITLE 26 // switch to next subtitle of movie. Can b used in videoFullScreen.xml window id=2005 +#define ACTION_SHOW_CODEC 27 // show information about file. Can b used in videoFullScreen.xml window id=2005 and in slideshow.xml window id=2007 +#define ACTION_NEXT_PICTURE 28 // show next picture of slideshow. Can b used in slideshow.xml window id=2007 +#define ACTION_PREV_PICTURE 29 // show previous picture of slideshow. Can b used in slideshow.xml window id=2007 +#define ACTION_ZOOM_OUT 30 // zoom in picture during slideshow. Can b used in slideshow.xml window id=2007 +#define ACTION_ZOOM_IN 31 // zoom out picture during slideshow. Can b used in slideshow.xml window id=2007 +#define ACTION_TOGGLE_SOURCE_DEST 32 // used to toggle between source view and destination view. Can be used in myfiles.xml window id=3 +#define ACTION_SHOW_PLAYLIST 33 // used to toggle between current view and playlist view. Can b used in all mymusic xml files +#define ACTION_QUEUE_ITEM 34 // used to queue a item to the playlist. Can b used in all mymusic xml files +#define ACTION_REMOVE_ITEM 35 // not used anymore +#define ACTION_SHOW_FULLSCREEN 36 // not used anymore +#define ACTION_ZOOM_LEVEL_NORMAL 37 // zoom 1x picture during slideshow. Can b used in slideshow.xml window id=2007 +#define ACTION_ZOOM_LEVEL_1 38 // zoom 2x picture during slideshow. Can b used in slideshow.xml window id=2007 +#define ACTION_ZOOM_LEVEL_2 39 // zoom 3x picture during slideshow. Can b used in slideshow.xml window id=2007 +#define ACTION_ZOOM_LEVEL_3 40 // zoom 4x picture during slideshow. Can b used in slideshow.xml window id=2007 +#define ACTION_ZOOM_LEVEL_4 41 // zoom 5x picture during slideshow. Can b used in slideshow.xml window id=2007 +#define ACTION_ZOOM_LEVEL_5 42 // zoom 6x picture during slideshow. Can b used in slideshow.xml window id=2007 +#define ACTION_ZOOM_LEVEL_6 43 // zoom 7x picture during slideshow. Can b used in slideshow.xml window id=2007 +#define ACTION_ZOOM_LEVEL_7 44 // zoom 8x picture during slideshow. Can b used in slideshow.xml window id=2007 +#define ACTION_ZOOM_LEVEL_8 45 // zoom 9x picture during slideshow. Can b used in slideshow.xml window id=2007 +#define ACTION_ZOOM_LEVEL_9 46 // zoom 10x picture during slideshow. Can b used in slideshow.xml window id=2007 + +#define ACTION_CALIBRATE_SWAP_ARROWS 47 // select next arrow. Can b used in: settingsScreenCalibration.xml windowid=11 +#define ACTION_CALIBRATE_RESET 48 // reset calibration to defaults. Can b used in: settingsScreenCalibration.xml windowid=11/settingsUICalibration.xml windowid=10 +#define ACTION_ANALOG_MOVE 49 // analog thumbstick move. Can b used in: slideshow.xml window id=2007/settingsScreenCalibration.xml windowid=11/settingsUICalibration.xml windowid=10 +#define ACTION_ROTATE_PICTURE 50 // rotate current picture during slideshow. Can b used in slideshow.xml window id=2007 +#define ACTION_CLOSE_DIALOG 51 // action for closing the dialog. Can b used in any dialog +#define ACTION_SUBTITLE_DELAY_MIN 52 // Decrease subtitle/movie Delay. Can b used in videoFullScreen.xml window id=2005 +#define ACTION_SUBTITLE_DELAY_PLUS 53 // Increase subtitle/movie Delay. Can b used in videoFullScreen.xml window id=2005 +#define ACTION_AUDIO_DELAY_MIN 54 // Increase avsync delay. Can b used in videoFullScreen.xml window id=2005 +#define ACTION_AUDIO_DELAY_PLUS 55 // Decrease avsync delay. Can b used in videoFullScreen.xml window id=2005 +#define ACTION_AUDIO_NEXT_LANGUAGE 56 // Select next language in movie. Can b used in videoFullScreen.xml window id=2005 +#define ACTION_CHANGE_RESOLUTION 57 // switch 2 next resolution. Can b used during screen calibration settingsScreenCalibration.xml windowid=11 + +#define REMOTE_0 58 // remote keys 0-9. are used by multiple windows +#define REMOTE_1 59 // for example in videoFullScreen.xml window id=2005 you can +#define REMOTE_2 60 // enter time (mmss) to jump to particular point in the movie +#define REMOTE_3 61 +#define REMOTE_4 62 // with spincontrols you can enter 3digit number to quickly set +#define REMOTE_5 63 // spincontrol to desired value +#define REMOTE_6 64 +#define REMOTE_7 65 +#define REMOTE_8 66 +#define REMOTE_9 67 + +#define ACTION_PLAY 68 // Unused at the moment +#define ACTION_OSD_SHOW_LEFT 69 // Move left in OSD. Can b used in videoFullScreen.xml window id=2005 +#define ACTION_OSD_SHOW_RIGHT 70 // Move right in OSD. Can b used in videoFullScreen.xml window id=2005 +#define ACTION_OSD_SHOW_UP 71 // Move up in OSD. Can b used in videoFullScreen.xml window id=2005 +#define ACTION_OSD_SHOW_DOWN 72 // Move down in OSD. Can b used in videoFullScreen.xml window id=2005 +#define ACTION_OSD_SHOW_SELECT 73 // toggle/select option in OSD. Can b used in videoFullScreen.xml window id=2005 +#define ACTION_OSD_SHOW_VALUE_PLUS 74 // increase value of current option in OSD. Can b used in videoFullScreen.xml window id=2005 +#define ACTION_OSD_SHOW_VALUE_MIN 75 // decrease value of current option in OSD. Can b used in videoFullScreen.xml window id=2005 +#define ACTION_SMALL_STEP_BACK 76 // jumps a few seconds back during playback of movie. Can b used in videoFullScreen.xml window id=2005 + +#define ACTION_PLAYER_FORWARD 77 // FF in current file played. global action, can be used anywhere +#define ACTION_PLAYER_REWIND 78 // RW in current file played. global action, can be used anywhere +#define ACTION_PLAYER_PLAY 79 // Play current song. Unpauses song and sets playspeed to 1x. global action, can be used anywhere + +#define ACTION_DELETE_ITEM 80 // delete current selected item. Can be used in myfiles.xml window id=3 and in myvideoTitle.xml window id=25 +#define ACTION_COPY_ITEM 81 // copy current selected item. Can be used in myfiles.xml window id=3 +#define ACTION_MOVE_ITEM 82 // move current selected item. Can be used in myfiles.xml window id=3 +#define ACTION_SHOW_MPLAYER_OSD 83 // toggles mplayers OSD. Can be used in videofullscreen.xml window id=2005 +#define ACTION_OSD_HIDESUBMENU 84 // removes an OSD sub menu. Can be used in videoOSD.xml window id=2901 +#define ACTION_TAKE_SCREENSHOT 85 // take a screenshot +#define ACTION_RENAME_ITEM 87 // rename item + +#define ACTION_VOLUME_UP 88 +#define ACTION_VOLUME_DOWN 89 +#define ACTION_MUTE 91 + +#define ACTION_MOUSE 90 + +#define ACTION_MOUSE_CLICK 100 +#define ACTION_MOUSE_LEFT_CLICK 100 +#define ACTION_MOUSE_RIGHT_CLICK 101 +#define ACTION_MOUSE_MIDDLE_CLICK 102 +#define ACTION_MOUSE_XBUTTON1_CLICK 103 +#define ACTION_MOUSE_XBUTTON2_CLICK 104 + +#define ACTION_MOUSE_DOUBLE_CLICK 105 +#define ACTION_MOUSE_LEFT_DOUBLE_CLICK 105 +#define ACTION_MOUSE_RIGHT_DOUBLE_CLICK 106 +#define ACTION_MOUSE_MIDDLE_DOUBLE_CLICK 107 +#define ACTION_MOUSE_XBUTTON1_DOUBLE_CLICK 108 +#define ACTION_MOUSE_XBUTTON2_DOUBLE_CLICK 109 + +#define ACTION_BACKSPACE 110 +#define ACTION_SCROLL_UP 111 +#define ACTION_SCROLL_DOWN 112 +#define ACTION_ANALOG_FORWARD 113 +#define ACTION_ANALOG_REWIND 114 + +#define ACTION_MOVE_ITEM_UP 115 // move item up in playlist +#define ACTION_MOVE_ITEM_DOWN 116 // move item down in playlist +#define ACTION_CONTEXT_MENU 117 // pops up the context menu + + +// stuff for virtual keyboard shortcuts +#define ACTION_SHIFT 118 +#define ACTION_SYMBOLS 119 +#define ACTION_CURSOR_LEFT 120 +#define ACTION_CURSOR_RIGHT 121 + +#define ACTION_BUILT_IN_FUNCTION 122 + +#define ACTION_SHOW_OSD_TIME 123 // displays current time, can be used in videoFullScreen.xml window id=2005 +#define ACTION_ANALOG_SEEK_FORWARD 124 // seeks forward, and displays the seek bar. +#define ACTION_ANALOG_SEEK_BACK 125 // seeks backward, and displays the seek bar. + +#define ACTION_VIS_PRESET_SHOW 126 +#define ACTION_VIS_PRESET_LIST 127 +#define ACTION_VIS_PRESET_NEXT 128 +#define ACTION_VIS_PRESET_PREV 129 +#define ACTION_VIS_PRESET_LOCK 130 +#define ACTION_VIS_PRESET_RANDOM 131 +#define ACTION_VIS_RATE_PRESET_PLUS 132 +#define ACTION_VIS_RATE_PRESET_MINUS 133 + +#define ACTION_SHOW_VIDEOMENU 134 +#define ACTION_ENTER 135 + +#define ACTION_INCREASE_RATING 136 +#define ACTION_DECREASE_RATING 137 + +#define ACTION_NEXT_SCENE 138 // switch to next scene/cutpoint in movie +#define ACTION_PREV_SCENE 139 // switch to previous scene/cutpoint in movie + +#define ACTION_NEXT_LETTER 140 // jump through a list or container by letter +#define ACTION_PREV_LETTER 141 + +#define ACTION_JUMP_SMS2 142 // jump direct to a particular letter using SMS-style input +#define ACTION_JUMP_SMS3 143 +#define ACTION_JUMP_SMS4 144 +#define ACTION_JUMP_SMS5 145 +#define ACTION_JUMP_SMS6 146 +#define ACTION_JUMP_SMS7 147 +#define ACTION_JUMP_SMS8 148 +#define ACTION_JUMP_SMS9 149 + +#define ACTION_FILTER_CLEAR 150 +#define ACTION_FILTER_SMS2 151 +#define ACTION_FILTER_SMS3 152 +#define ACTION_FILTER_SMS4 153 +#define ACTION_FILTER_SMS5 154 +#define ACTION_FILTER_SMS6 155 +#define ACTION_FILTER_SMS7 156 +#define ACTION_FILTER_SMS8 157 +#define ACTION_FILTER_SMS9 158 + +#define ACTION_FIRST_PAGE 159 +#define ACTION_LAST_PAGE 160 + +#define ACTION_AUDIO_DELAY 161 +#define ACTION_SUBTITLE_DELAY 162 + +#define ACTION_PASTE 180 +#define ACTION_NEXT_CONTROL 181 +#define ACTION_PREV_CONTROL 182 +#define ACTION_CHANNEL_SWITCH 183 + +#define ACTION_TOGGLE_FULLSCREEN 199 // switch 2 desktop resolution +#define ACTION_TOGGLE_WATCHED 200 // Toggle watched status (videos) +#define ACTION_SCAN_ITEM 201 // scan item +#define ACTION_TOGGLE_DIGITAL_ANALOG 202 // switch digital <-> analog +#define ACTION_RELOAD_KEYMAPS 203 // reloads CButtonTranslator's keymaps +#define ACTION_GUIPROFILE_BEGIN 204 // start the GUIControlProfiler running + +// Window ID defines to make the code a bit more readable +#define WINDOW_INVALID 9999 +#define WINDOW_HOME 10000 +#define WINDOW_PROGRAMS 10001 +#define WINDOW_PICTURES 10002 +#define WINDOW_FILES 10003 +#define WINDOW_SETTINGS_MENU 10004 +#define WINDOW_MUSIC 10005 // virtual window to return the music start window. +#define WINDOW_VIDEOS 10006 +#define WINDOW_SYSTEM_INFORMATION 10007 +#define WINDOW_TEST_PATTERN 10008 +#define WINDOW_SCREEN_CALIBRATION 10011 + +#define WINDOW_SETTINGS_MYPICTURES 10012 +#define WINDOW_SETTINGS_MYPROGRAMS 10013 +#define WINDOW_SETTINGS_MYWEATHER 10014 +#define WINDOW_SETTINGS_MYMUSIC 10015 +#define WINDOW_SETTINGS_SYSTEM 10016 +#define WINDOW_SETTINGS_MYVIDEOS 10017 +#define WINDOW_SETTINGS_NETWORK 10018 +#define WINDOW_SETTINGS_APPEARANCE 10019 + +#define WINDOW_SCRIPTS 10020 +#define WINDOW_VIDEO_GENRE 10021 +#define WINDOW_VIDEO_ACTOR 10022 +#define WINDOW_VIDEO_YEAR 10023 +#define WINDOW_VIDEO_FILES 10024 +#define WINDOW_VIDEO_NAV 10025 +#define WINDOW_VIDEO_PLAYLIST 10028 + +#define WINDOW_LOGIN_SCREEN 10029 +#define WINDOW_SETTINGS_PROFILES 10034 + +#define WINDOW_DIALOG_YES_NO 10100 +#define WINDOW_DIALOG_PROGRESS 10101 +#define WINDOW_DIALOG_KEYBOARD 10103 +#define WINDOW_DIALOG_VOLUME_BAR 10104 +#define WINDOW_DIALOG_SUB_MENU 10105 +#define WINDOW_DIALOG_CONTEXT_MENU 10106 +#define WINDOW_DIALOG_KAI_TOAST 10107 +#define WINDOW_DIALOG_NUMERIC 10109 +#define WINDOW_DIALOG_GAMEPAD 10110 +#define WINDOW_DIALOG_BUTTON_MENU 10111 +#define WINDOW_DIALOG_MUSIC_SCAN 10112 +#define WINDOW_DIALOG_MUTE_BUG 10113 +#define WINDOW_DIALOG_PLAYER_CONTROLS 10114 +#define WINDOW_DIALOG_SEEK_BAR 10115 +#define WINDOW_DIALOG_MUSIC_OSD 10120 +#define WINDOW_DIALOG_VIS_SETTINGS 10121 +#define WINDOW_DIALOG_VIS_PRESET_LIST 10122 +#define WINDOW_DIALOG_VIDEO_OSD_SETTINGS 10123 +#define WINDOW_DIALOG_AUDIO_OSD_SETTINGS 10124 +#define WINDOW_DIALOG_VIDEO_BOOKMARKS 10125 +#define WINDOW_DIALOG_FILE_BROWSER 10126 +#define WINDOW_DIALOG_NETWORK_SETUP 10128 +#define WINDOW_DIALOG_MEDIA_SOURCE 10129 +#define WINDOW_DIALOG_PROFILE_SETTINGS 10130 +#define WINDOW_DIALOG_LOCK_SETTINGS 10131 +#define WINDOW_DIALOG_CONTENT_SETTINGS 10132 +#define WINDOW_DIALOG_VIDEO_SCAN 10133 +#define WINDOW_DIALOG_FAVOURITES 10134 +#define WINDOW_DIALOG_SONG_INFO 10135 +#define WINDOW_DIALOG_SMART_PLAYLIST_EDITOR 10136 +#define WINDOW_DIALOG_SMART_PLAYLIST_RULE 10137 +#define WINDOW_DIALOG_BUSY 10138 +#define WINDOW_DIALOG_PICTURE_INFO 10139 +#define WINDOW_DIALOG_PLUGIN_SETTINGS 10140 +#define WINDOW_DIALOG_ACCESS_POINTS 10141 +#define WINDOW_DIALOG_FULLSCREEN_INFO 10142 +#define WINDOW_DIALOG_KARAOKE_SONGSELECT 10143 +#define WINDOW_DIALOG_KARAOKE_SELECTOR 10144 +#define WINDOW_DIALOG_SLIDER 10145 + +#define WINDOW_MUSIC_PLAYLIST 10500 +#define WINDOW_MUSIC_FILES 10501 +#define WINDOW_MUSIC_NAV 10502 +#define WINDOW_MUSIC_PLAYLIST_EDITOR 10503 + +//#define WINDOW_VIRTUAL_KEYBOARD 11000 +#define WINDOW_DIALOG_SELECT 12000 +#define WINDOW_MUSIC_INFO 12001 +#define WINDOW_DIALOG_OK 12002 +#define WINDOW_VIDEO_INFO 12003 +#define WINDOW_SCRIPTS_INFO 12004 +#define WINDOW_FULLSCREEN_VIDEO 12005 +#define WINDOW_VISUALISATION 12006 +#define WINDOW_SLIDESHOW 12007 +#define WINDOW_DIALOG_FILESTACKING 12008 +#define WINDOW_KARAOKELYRICS 12009 +#define WINDOW_WEATHER 12600 +#define WINDOW_SCREENSAVER 12900 +#define WINDOW_OSD 12901 + +#define WINDOW_VIDEO_MENU 12902 +#define WINDOW_MUSIC_OVERLAY 12903 +#define WINDOW_VIDEO_OVERLAY 12904 + +#define WINDOW_STARTUP 12999 // for startup animations + +// WINDOW_ID's from 13000 to 13099 reserved for Python + +#define WINDOW_PYTHON_START 13000 +#define WINDOW_PYTHON_END 13099 + +#define ICON_TYPE_NONE 101 +#define ICON_TYPE_PROGRAMS 102 +#define ICON_TYPE_MUSIC 103 +#define ICON_TYPE_PICTURES 104 +#define ICON_TYPE_VIDEOS 105 +#define ICON_TYPE_FILES 106 +#define ICON_TYPE_WEATHER 107 +#define ICON_TYPE_SETTINGS 109 + +/*! + \ingroup actionkeys + \brief + */ +class CAction +{ +public: + CAction() + { + id = 0; + amount1 = amount2 = repeat = 0; + buttonCode = 0; + unicode = 0; + holdTime = 0; + }; + int id; + float amount1; + float amount2; + float repeat; + unsigned int buttonCode; + CStdString strAction; + wchar_t unicode; // new feature, does not fit into id like ASCII, wouldn't be good design either!? Will be set whenever ASCII is set into id (for backwards compatibility) + unsigned int holdTime; ///< Time the key has been held down (in ms) +}; + +/*! + \ingroup actionkeys + \brief + */ +class CKey +{ +public: + CKey(void); + CKey(uint32_t buttonCode, uint8_t leftTrigger = 0, uint8_t rightTrigger = 0, float leftThumbX = 0.0f, float leftThumbY = 0.0f, float rightThumbX = 0.0f, float rightThumbY = 0.0f, float repeat = 0.0f); + CKey(const CKey& key); + + virtual ~CKey(void); + const CKey& operator=(const CKey& key); + uint32_t GetButtonCode() const; // for backwards compatibility only + uint32_t GetUnicode() const; // http api does not really support unicode till now. It only simulates unicode when ascii codes are available: + uint8_t GetLeftTrigger() const; + uint8_t GetRightTrigger() const; + float GetLeftThumbX() const; + float GetLeftThumbY() const; + float GetRightThumbX() const; + float GetRightThumbY() const; + float GetRepeat() const; + bool FromKeyboard() const; + bool IsAnalogButton() const; + bool IsIRRemote() const; + void SetFromHttpApi(bool); + bool GetFromHttpApi() const; + + void SetHeld(unsigned int held); + unsigned int GetHeld() const; +private: + uint32_t m_buttonCode; + uint8_t m_leftTrigger; + uint8_t m_rightTrigger; + float m_leftThumbX; + float m_leftThumbY; + float m_rightThumbX; + float m_rightThumbY; + float m_repeat; // time since last keypress + bool m_fromHttpApi; + unsigned int m_held; +}; +#endif + diff --git a/guilib/LocalizeStrings.cpp b/guilib/LocalizeStrings.cpp new file mode 100644 index 0000000000..78f10f15f4 --- /dev/null +++ b/guilib/LocalizeStrings.cpp @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" +#include "LocalizeStrings.h" +#include "utils/CharsetConverter.h" +#include "utils/log.h" +#include "FileSystem/SpecialProtocol.h" +#include "XMLUtils.h" + +CLocalizeStrings g_localizeStrings; +CLocalizeStrings g_localizeStringsTemp; +extern CStdString g_LoadErrorStr; + +CLocalizeStrings::CLocalizeStrings(void) +{ + +} + +CLocalizeStrings::~CLocalizeStrings(void) +{ + +} + +CStdString CLocalizeStrings::ToUTF8(const CStdString& strEncoding, const CStdString& str) +{ + if (strEncoding.IsEmpty()) + return str; + + CStdString ret; + g_charsetConverter.stringCharsetToUtf8(strEncoding, str, ret); + return ret; +} + +void CLocalizeStrings::ClearSkinStrings() +{ + // clear the skin strings + Clear(31000, 31999); +} + +bool CLocalizeStrings::LoadSkinStrings(const CStdString& path, const CStdString& fallbackPath) +{ + ClearSkinStrings(); + // load the skin strings in. + CStdString encoding, error; + if (!LoadXML(path, encoding, error)) + { + if (path == fallbackPath) // no fallback, nothing to do + return false; + } + + // load the fallback + if (path != fallbackPath) + LoadXML(fallbackPath, encoding, error); + + return true; +} + +bool CLocalizeStrings::LoadXML(const CStdString &filename, CStdString &encoding, CStdString &error, uint32_t offset /* = 0 */) +{ + TiXmlDocument xmlDoc; + if (!xmlDoc.LoadFile(PTH_IC(filename))) + { + CLog::Log(LOGDEBUG, "unable to load %s: %s at line %d", filename.c_str(), xmlDoc.ErrorDesc(), xmlDoc.ErrorRow()); + error.Format("Unable to load %s: %s at line %d", filename.c_str(), xmlDoc.ErrorDesc(), xmlDoc.ErrorRow()); + return false; + } + + XMLUtils::GetEncoding(&xmlDoc, encoding); + + TiXmlElement* pRootElement = xmlDoc.RootElement(); + if (!pRootElement || pRootElement->NoChildren() || + pRootElement->ValueStr()!=CStdString("strings")) + { + CLog::Log(LOGERROR, "%s Doesn't contain <strings>", filename.c_str()); + error.Format("%s\nDoesnt start with <strings>", filename.c_str()); + return false; + } + + const TiXmlElement *pChild = pRootElement->FirstChildElement("string"); + while (pChild) + { + // Load new style language file with id as attribute + const char* attrId=pChild->Attribute("id"); + if (attrId && !pChild->NoChildren()) + { + int id = atoi(attrId) + offset; + if (m_strings.find(id) == m_strings.end()) + m_strings[id] = ToUTF8(encoding, pChild->FirstChild()->Value()); + } + pChild = pChild->NextSiblingElement("string"); + } + return true; +} + +bool CLocalizeStrings::Load(const CStdString& strFileName, const CStdString& strFallbackFileName) +{ + bool bLoadFallback = !strFileName.Equals(strFallbackFileName); + + CStdString encoding, error; + Clear(); + + if (!LoadXML(strFileName, encoding, error)) + { + // try loading the fallback + if (!bLoadFallback || !LoadXML(strFallbackFileName, encoding, error)) + { + g_LoadErrorStr = error; + return false; + } + bLoadFallback = false; + } + + if (bLoadFallback) + LoadXML(strFallbackFileName, encoding, error); + + // fill in the constant strings + m_strings[20022] = ""; + m_strings[20027] = ToUTF8(encoding, "°F"); + m_strings[20028] = ToUTF8(encoding, "K"); + m_strings[20029] = ToUTF8(encoding, "°C"); + m_strings[20030] = ToUTF8(encoding, "°Ré"); + m_strings[20031] = ToUTF8(encoding, "°Ra"); + m_strings[20032] = ToUTF8(encoding, "°Rø"); + m_strings[20033] = ToUTF8(encoding, "°De"); + m_strings[20034] = ToUTF8(encoding, "°N"); + + m_strings[20200] = ToUTF8(encoding, "km/h"); + m_strings[20201] = ToUTF8(encoding, "m/min"); + m_strings[20202] = ToUTF8(encoding, "m/s"); + m_strings[20203] = ToUTF8(encoding, "ft/h"); + m_strings[20204] = ToUTF8(encoding, "ft/min"); + m_strings[20205] = ToUTF8(encoding, "ft/s"); + m_strings[20206] = ToUTF8(encoding, "mph"); + m_strings[20207] = ToUTF8(encoding, "kts"); + m_strings[20208] = ToUTF8(encoding, "Beaufort"); + m_strings[20209] = ToUTF8(encoding, "inch/s"); + m_strings[20210] = ToUTF8(encoding, "yard/s"); + m_strings[20211] = ToUTF8(encoding, "Furlong/Fortnight"); + + return true; +} + +static CStdString szEmptyString = ""; + +const CStdString& CLocalizeStrings::Get(uint32_t dwCode) const +{ + ciStrings i = m_strings.find(dwCode); + if (i == m_strings.end()) + { + return szEmptyString; + } + return i->second; +} + +void CLocalizeStrings::Clear() +{ + m_strings.clear(); +} + +void CLocalizeStrings::Clear(uint32_t start, uint32_t end) +{ + iStrings it = m_strings.begin(); + while (it != m_strings.end()) + { + if (it->first >= start && it->first <= end) + m_strings.erase(it++); + else + ++it; + } +} + +uint32_t CLocalizeStrings::LoadBlock(const CStdString &id, const CStdString &path, const CStdString &fallbackPath) +{ + iBlocks it = m_blocks.find(id); + if (it != m_blocks.end()) + return it->second; // already loaded + + // grab a new block + uint32_t offset = block_start + m_blocks.size()*block_size; + m_blocks.insert(make_pair(id, offset)); + + // load the strings + CStdString encoding, error; + bool success = LoadXML(path, encoding, error, offset); + if (!success) + { + if (path == fallbackPath) // no fallback, nothing to do + return 0; + } + + // load the fallback + if (path != fallbackPath) + success |= LoadXML(fallbackPath, encoding, error, offset); + + return success ? offset : 0; +} + +void CLocalizeStrings::ClearBlock(const CStdString &id) +{ + iBlocks it = m_blocks.find(id); + if (it == m_blocks.end()) + { + CLog::Log(LOGERROR, "%s: Trying to clear non existent block %s", __FUNCTION__, id.c_str()); + return; // doesn't exist + } + + // clear our block + Clear(it->second, it->second + block_size); + m_blocks.erase(it); +} diff --git a/guilib/LocalizeStrings.h b/guilib/LocalizeStrings.h new file mode 100644 index 0000000000..0cb8eb731e --- /dev/null +++ b/guilib/LocalizeStrings.h @@ -0,0 +1,72 @@ +/*! +\file LocalizeStrings.h +\brief +*/ + +#ifndef GUILIB_LOCALIZESTRINGS_H +#define GUILIB_LOCALIZESTRINGS_H + +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "StdString.h" + +#include <map> + +/*! + \ingroup strings + \brief + */ +class CLocalizeStrings +{ +public: + CLocalizeStrings(void); + virtual ~CLocalizeStrings(void); + bool Load(const CStdString& strFileName, const CStdString& strFallbackFileName="special://xbmc/language/english/strings.xml"); + bool LoadSkinStrings(const CStdString& path, const CStdString& fallbackPath); + void ClearSkinStrings(); + const CStdString& Get(uint32_t code) const; + void Clear(); + uint32_t LoadBlock(const CStdString &id, const CStdString &path, const CStdString &fallbackPath); + void ClearBlock(const CStdString &id); +protected: + void Clear(uint32_t start, uint32_t end); + bool LoadXML(const CStdString &filename, CStdString &encoding, CStdString &error, uint32_t offset = 0); + CStdString ToUTF8(const CStdString &encoding, const CStdString &str); + std::map<uint32_t, CStdString> m_strings; + typedef std::map<uint32_t, CStdString>::const_iterator ciStrings; + typedef std::map<uint32_t, CStdString>::iterator iStrings; + + static const uint32_t block_start = 0xf000000; + static const uint32_t block_size = 4096; + std::map<CStdString, uint32_t> m_blocks; + typedef std::map<CStdString, uint32_t>::iterator iBlocks; +}; + +/*! + \ingroup strings + \brief + */ +extern CLocalizeStrings g_localizeStrings; +extern CLocalizeStrings g_localizeStringsTemp; +#endif diff --git a/guilib/Makefile b/guilib/Makefile new file mode 100644 index 0000000000..3f209f9478 --- /dev/null +++ b/guilib/Makefile @@ -0,0 +1,87 @@ +INCLUDES=-I. -I../ -Icommon -I../xbmc -I../xbmc/cores -I../xbmc/linux -I../xbmc/utils -I/usr/include/freetype2 -I/usr/include/SDL + +SRCS=AnimatedGif.cpp \ + AudioContext.cpp \ + DirectXGraphics.cpp \ + GraphicContext.cpp \ + GUIAudioManager.cpp \ + GUIBaseContainer.cpp \ + GUIButtonControl.cpp \ + GUIButtonScroller.cpp \ + GUICheckMarkControl.cpp \ + GUIControl.cpp \ + GUIControlFactory.cpp \ + GUIControlGroup.cpp \ + GUIControlGroupList.cpp \ + GUIDialog.cpp \ + GUIEditControl.cpp \ + GUIFadeLabelControl.cpp \ + GUIFixedListContainer.cpp \ + GUIFont.cpp \ + GUIFontManager.cpp \ + GUIFontTTF.cpp \ + GUIImage.cpp \ + GUIIncludes.cpp \ + GUILabelControl.cpp \ + GUIListContainer.cpp \ + GUIListGroup.cpp \ + GUIListItem.cpp \ + GUIListItemLayout.cpp \ + GUIMessage.cpp \ + GUIMoverControl.cpp \ + GUIMultiImage.cpp \ + GUIPanelContainer.cpp \ + GUIProgressControl.cpp \ + GUIRadioButtonControl.cpp \ + GUIResizeControl.cpp \ + GUIRSSControl.cpp \ + GUIScrollBarControl.cpp \ + GUISelectButtonControl.cpp \ + GUISettingsSliderControl.cpp \ + GUISliderControl.cpp \ + GUISpinControl.cpp \ + GUISpinControlEx.cpp \ + GUIStandardWindow.cpp \ + GUITextBox.cpp \ + GUITexture.cpp \ + GUITextureSDL.cpp \ + GUITextureGL.cpp \ + GUITextureGLES.cpp \ + GUIToggleButtonControl.cpp \ + GUIVideoControl.cpp \ + GUIVisualisationControl.cpp \ + GUIWindow.cpp \ + GUIWindowManager.cpp \ + GUIWrappingListContainer.cpp \ + IWindowManagerCallback.cpp \ + Key.cpp \ + LocalizeStrings.cpp \ + SkinInfo.cpp \ + TextureBundle.cpp \ + TextureManager.cpp \ + VisibleEffect.cpp \ + XMLUtils.cpp \ + GUISound.cpp \ + GUIColorManager.cpp \ + FrameBufferObject.cpp \ + Shader.cpp \ + GUIListLabel.cpp \ + GUIBorderedImage.cpp \ + GUITextLayout.cpp \ + GUIMultiSelectText.cpp \ + GUIInfoColor.cpp \ + GUIFontTTFGL.cpp \ + GUIFontTTFGLES.cpp \ + Texture.cpp \ + TextureGL.cpp \ + GUIControlProfiler.cpp \ + +LIB=guilib.a + +include ../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) + +try : try.o TextureBundle.o DirectXGraphics.o GUIFontTTF.o + g++ -o try try.o TextureBundle.o DirectXGraphics.o GraphicContext.o GUIIncludes.o ../xbmc/utils/CriticalSection.o ../xbmc/XBVideoConfig.o SkinInfo.o tinyXML/tinyxml.a ../xbmc/linux/CriticalSection.o GUIFontTTF.o GUIFontBase.o GUIFontManager.o GUIFont.o XMLUtils.o GUIImage.o GUIControl.o TextureManager.o GUIMessage.o ../xbmc/utils/SingleLock.o VisibleEffect.o GUIWindowManager.o AnimatedGif.o -lSDL_image -lSDL_gfx -lSDL -llzo -lfreetype + + diff --git a/guilib/Resolution.h b/guilib/Resolution.h new file mode 100644 index 0000000000..cd64fa7410 --- /dev/null +++ b/guilib/Resolution.h @@ -0,0 +1,99 @@ +/*
+* Copyright (C) 2005-2008 Team XBMC
+* http://www.xbmc.org
+*
+* 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, 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 XBMC; see the file COPYING. If not, write to
+* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+* http://www.gnu.org/copyleft/gpl.html
+*
+*/
+
+#pragma once
+
+#include <stdint.h>
+#include "StdString.h"
+
+enum RESOLUTION {
+ RES_INVALID = -1,
+ RES_HDTV_1080i = 0,
+ RES_HDTV_720p = 1,
+ RES_HDTV_480p_4x3 = 2,
+ RES_HDTV_480p_16x9 = 3,
+ RES_NTSC_4x3 = 4,
+ RES_NTSC_16x9 = 5,
+ RES_PAL_4x3 = 6,
+ RES_PAL_16x9 = 7,
+ RES_PAL60_4x3 = 8,
+ RES_PAL60_16x9 = 9,
+ RES_AUTORES = 10,
+ RES_WINDOW = 11,
+ RES_DESKTOP = 12,
+ RES_CUSTOM = 13
+};
+
+enum VSYNC {
+ VSYNC_DISABLED = 0,
+ VSYNC_VIDEO = 1,
+ VSYNC_ALWAYS = 2,
+ VSYNC_DRIVER = 3
+};
+
+struct OVERSCAN
+{
+ int left;
+ int top;
+ int right;
+ int bottom;
+public:
+ OVERSCAN()
+ {
+ left = top = right = bottom = 0;
+ }
+ OVERSCAN(const OVERSCAN& os)
+ {
+ left = os.left; top = os.top;
+ right = os.right; bottom = os.bottom;
+ }
+};
+
+struct RESOLUTION_INFO
+{
+ OVERSCAN Overscan;
+ bool bFullScreen;
+ int iScreen;
+ int iWidth;
+ int iHeight;
+ int iSubtitles;
+ uint32_t dwFlags;
+ float fPixelRatio;
+ float fRefreshRate;
+ CStdString strMode;
+ CStdString strOutput;
+ CStdString strId;
+ public:
+ RESOLUTION_INFO()
+ {
+ bFullScreen = false;
+ iScreen = iWidth = iHeight = iSubtitles = dwFlags = 0;
+ fPixelRatio = fRefreshRate = 0.f;
+ }
+ RESOLUTION_INFO(const RESOLUTION_INFO& res)
+ {
+ Overscan = res.Overscan; bFullScreen = res.bFullScreen;
+ iScreen = res.iScreen; iWidth = res.iWidth; iHeight = res.iHeight;
+ iSubtitles = res.iSubtitles; dwFlags = res.dwFlags;
+ fPixelRatio = res.fPixelRatio; fRefreshRate = res.fRefreshRate;
+ strMode = res.strMode; strOutput = res.strOutput; strId = res.strId;
+ }
+};
diff --git a/guilib/Shader.cpp b/guilib/Shader.cpp new file mode 100644 index 0000000000..d492278865 --- /dev/null +++ b/guilib/Shader.cpp @@ -0,0 +1,534 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" + +#if defined(HAS_GL) || HAS_GLES == 2 +#include "../xbmc/Settings.h" +#include "../xbmc/FileSystem/File.h" +#include "Shader.h" +#include "utils/log.h" + +#define LOG_SIZE 1024 + +using namespace Shaders; +using namespace XFILE; +using namespace std; + +////////////////////////////////////////////////////////////////////// +// CShader +////////////////////////////////////////////////////////////////////// +bool CShader::LoadSource(const string& filename, const string& prefix) +{ + if(filename.empty()) + return true; + + CFileStream file; + + if(!file.Open("special://xbmc/system/shaders/" + filename)) + { + CLog::Log(LOGERROR, "CYUVShaderGLSL::CYUVShaderGLSL - failed to open file %s", filename.c_str()); + return false; + } + + getline(file, m_source, '\0'); + m_source.insert(0, prefix); + return true; +} + +////////////////////////////////////////////////////////////////////// +// CGLSLVertexShader +////////////////////////////////////////////////////////////////////// + +bool CGLSLVertexShader::Compile() +{ + GLint params[4]; + + Free(); + +#ifdef HAS_GL + if(!GLEW_VERSION_2_0) + { + CLog::Log(LOGERROR, "GL: GLSL vertex shaders not supported"); + return false; + } +#endif + + /* + Workaround for locale bug in nVidia's shader compiler. + Save the current locale, set to a neutral locale while compiling and switch back afterwards. + */ + char * currentLocale = setlocale(LC_NUMERIC, NULL); + setlocale(LC_NUMERIC, "C"); + + m_vertexShader = glCreateShader(GL_VERTEX_SHADER); + const char *ptr = m_source.c_str(); + glShaderSource(m_vertexShader, 1, &ptr, 0); + glCompileShader(m_vertexShader); + glGetShaderiv(m_vertexShader, GL_COMPILE_STATUS, params); + VerifyGLState(); + if (params[0]!=GL_TRUE) + { + GLchar log[LOG_SIZE]; + CLog::Log(LOGERROR, "GL: Error compiling shader"); + glGetShaderInfoLog(m_vertexShader, LOG_SIZE, NULL, log); + CLog::Log(LOGERROR, "%s", log); + m_lastLog = log; + m_compiled = false; + } + else + { + GLchar log[LOG_SIZE]; + CLog::Log(LOGDEBUG, "GL: Shader compilation log:"); + glGetShaderInfoLog(m_vertexShader, LOG_SIZE, NULL, log); + CLog::Log(LOGDEBUG, "%s", log); + m_lastLog = log; + m_compiled = true; + } + setlocale(LC_NUMERIC, currentLocale); + return m_compiled; +} + +void CGLSLVertexShader::Free() +{ +#ifdef HAS_GL + if(!GLEW_VERSION_2_0) + return; +#endif + + if (m_vertexShader) + glDeleteShader(m_vertexShader); + m_vertexShader = 0; +} + +#ifndef HAS_GLES + +////////////////////////////////////////////////////////////////////// +// CARBVertexShader +////////////////////////////////////////////////////////////////////// +bool CARBVertexShader::Compile() +{ + GLint err = 0; + + Free(); + + // Pixel shaders are not mandatory. + if (m_source.length()==0) + { + CLog::Log(LOGNOTICE, "GL: No vertex shader, fixed pipeline in use"); + return true; + } + + // Workaround for locale bug in nVidia's shader compiler. + // Save the current locale, set to a neutral while compiling and switch back afterwards. + + char * currentLocale = setlocale(LC_NUMERIC, NULL); + setlocale(LC_NUMERIC, "C"); + + glEnable(GL_VERTEX_PROGRAM_ARB); + glGenProgramsARB(1, &m_vertexShader); + glBindProgramARB(GL_VERTEX_PROGRAM_ARB, m_vertexShader); + + glProgramStringARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, + m_source.length(), m_source.c_str()); + + glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &err); + if (err>0) + { + CLog::Log(LOGERROR, "GL: Error compiling ARB vertex shader"); + m_compiled = false; + } + else + { + m_compiled = true; + } + glDisable(GL_VERTEX_PROGRAM_ARB); + setlocale(LC_NUMERIC, currentLocale); + return m_compiled; +} + +void CARBVertexShader::Free() +{ + if (m_vertexShader) + glDeleteProgramsARB(1, &m_vertexShader); + m_vertexShader = 0; +} +#endif + +////////////////////////////////////////////////////////////////////// +// CGLSLPixelShader +////////////////////////////////////////////////////////////////////// +bool CGLSLPixelShader::Compile() +{ +#ifdef HAS_GL + if(!GLEW_VERSION_2_0) + { + CLog::Log(LOGERROR, "GL: GLSL pixel shaders not supported"); + return false; + } +#endif + + GLint params[4]; + + Free(); + + // Pixel shaders are not mandatory. + if (m_source.length()==0) + { + CLog::Log(LOGNOTICE, "GL: No pixel shader, fixed pipeline in use"); + return true; + } + + /* + Workaround for locale bug in nVidia's shader compiler. + Save the current locale, set to a neutral while compiling and switch back afterwards. + */ + char * currentLocale = setlocale(LC_NUMERIC, NULL); + setlocale(LC_NUMERIC, "C"); + + m_pixelShader = glCreateShader(GL_FRAGMENT_SHADER); + const char *ptr = m_source.c_str(); + glShaderSource(m_pixelShader, 1, &ptr, 0); + glCompileShader(m_pixelShader); + glGetShaderiv(m_pixelShader, GL_COMPILE_STATUS, params); + if (params[0]!=GL_TRUE) + { + GLchar log[LOG_SIZE]; + CLog::Log(LOGERROR, "GL: Error compiling shader"); + glGetShaderInfoLog(m_pixelShader, LOG_SIZE, NULL, log); + CLog::Log(LOGERROR, "%s", log); + m_lastLog = log; + m_compiled = false; + } + else + { + GLchar log[LOG_SIZE]; + CLog::Log(LOGDEBUG, "GL: Shader compilation log:"); + glGetShaderInfoLog(m_pixelShader, LOG_SIZE, NULL, log); + CLog::Log(LOGDEBUG, "%s", log); + m_lastLog = log; + m_compiled = true; + } + setlocale(LC_NUMERIC, currentLocale); + return m_compiled; +} + +void CGLSLPixelShader::Free() +{ +#ifdef HAS_GL + if(!GLEW_VERSION_2_0) + return; +#endif + if (m_pixelShader) + glDeleteShader(m_pixelShader); + m_pixelShader = 0; +} + +#ifndef HAS_GLES + +////////////////////////////////////////////////////////////////////// +// CARBPixelShader +////////////////////////////////////////////////////////////////////// +bool CARBPixelShader::Compile() +{ + GLint err = 0; + + Free(); + + // Pixel shaders are not mandatory. + if (m_source.length()==0) + { + CLog::Log(LOGNOTICE, "GL: No pixel shader, fixed pipeline in use"); + return true; + } + + // Workaround for locale bug in nVidia's shader compiler. + // Save the current locale, set to a neutral while compiling and switch back afterwards. + char currentLocale[256]; + strncpy(currentLocale, setlocale(LC_NUMERIC, NULL), sizeof(currentLocale)); + setlocale(LC_NUMERIC, "C"); + + glEnable(GL_FRAGMENT_PROGRAM_ARB); + glGenProgramsARB(1, &m_pixelShader); + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_pixelShader); + + glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, + m_source.length(), m_source.c_str()); + + glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &err); + if (err>0) + { + CLog::Log(LOGERROR, "GL: Error compiling ARB pixel shader"); + m_compiled = false; + } + else + { + m_compiled = true; + } + glDisable(GL_FRAGMENT_PROGRAM_ARB); + setlocale(LC_NUMERIC, currentLocale); + return m_compiled; +} + +void CARBPixelShader::Free() +{ + if (m_pixelShader) + glDeleteProgramsARB(1, &m_pixelShader); + m_pixelShader = 0; +} + +#endif + +////////////////////////////////////////////////////////////////////// +// CGLSLShaderProgram +////////////////////////////////////////////////////////////////////// +void CGLSLShaderProgram::Free() +{ +#ifdef HAS_GL + if(!GLEW_VERSION_2_0) + return; +#endif + m_pVP->Free(); + VerifyGLState(); + m_pFP->Free(); + VerifyGLState(); + if (m_shaderProgram) + { + glDeleteProgram(m_shaderProgram); + } + m_shaderProgram = 0; + m_ok = false; + m_lastProgram = 0; +} + +bool CGLSLShaderProgram::CompileAndLink() +{ +#ifdef HAS_GL + // check that we support shaders + if(!GLEW_VERSION_2_0) + { + CLog::Log(LOGERROR, "GL: GLSL shaders not supported"); + return false; + } +#endif + + GLint params[4]; + + // free resources + Free(); + + // compiled vertex shader + if (!m_pVP->Compile()) + { + CLog::Log(LOGERROR, "GL: Error compiling vertex shader"); + return false; + } + + // compile pixel shader + if (!m_pFP->Compile()) + { + m_pVP->Free(); + CLog::Log(LOGERROR, "GL: Error compiling fragment shader"); + return false; + } + + // create program object + if (!(m_shaderProgram = glCreateProgram())) + { + CLog::Log(LOGERROR, "GL: Error creating shader program handle"); + goto error; + } + + // attach the vertex shader + glAttachShader(m_shaderProgram, m_pVP->Handle()); + VerifyGLState(); + + // if we have a pixel shader, attach it. If not, fixed pipeline + // will be used. + if (m_pFP->Handle()) + { + glAttachShader(m_shaderProgram, m_pFP->Handle()); + VerifyGLState(); + } + + // link the program + glLinkProgram(m_shaderProgram); + glGetProgramiv(m_shaderProgram, GL_LINK_STATUS, params); + if (params[0]!=GL_TRUE) + { + GLchar log[LOG_SIZE]; + CLog::Log(LOGERROR, "GL: Error linking shader"); + glGetProgramInfoLog(m_shaderProgram, LOG_SIZE, NULL, log); + CLog::Log(LOGERROR, "%s", log); + goto error; + } + VerifyGLState(); + + // validate the program + glValidateProgram(m_shaderProgram); + glGetProgramiv(m_shaderProgram, GL_VALIDATE_STATUS, params); + if (params[0]!=GL_TRUE) + { + GLchar log[LOG_SIZE]; + CLog::Log(LOGERROR, "GL: Error validating shader"); + glGetProgramInfoLog(m_shaderProgram, LOG_SIZE, NULL, log); + CLog::Log(LOGERROR, "%s", log); + goto error; + } + VerifyGLState(); + + m_ok = true; + OnCompiledAndLinked(); + VerifyGLState(); + return true; + + error: + m_ok = false; + Free(); + return false; +} + +bool CGLSLShaderProgram::Enable() +{ +#ifdef HAS_GL + if(!GLEW_VERSION_2_0) + return false; +#endif + + if (OK()) + { + glUseProgram(m_shaderProgram); + if (OnEnabled()) + { + VerifyGLState(); + return true; + } + else + { + glUseProgram(0); + return false; + } + return true; + } + return false; +} + +void CGLSLShaderProgram::Disable() +{ +#ifdef HAS_GL + if(!GLEW_VERSION_2_0) + return; +#endif + + if (OK()) + { + glUseProgram(0); + OnDisabled(); + } +} + +#ifndef HAS_GLES + +////////////////////////////////////////////////////////////////////// +// CARBShaderProgram +////////////////////////////////////////////////////////////////////// +void CARBShaderProgram::Free() +{ + m_pVP->Free(); + VerifyGLState(); + m_pFP->Free(); + VerifyGLState(); + m_ok = false; +} + +bool CARBShaderProgram::CompileAndLink() +{ + // free resources + Free(); + + // compiled vertex shader + if (!m_pVP->Compile()) + { + CLog::Log(LOGERROR, "GL: Error compiling vertex shader"); + goto error; + } + + // compile pixel shader + if (!m_pFP->Compile()) + { + m_pVP->Free(); + CLog::Log(LOGERROR, "GL: Error compiling fragment shader"); + goto error; + } + + m_ok = true; + OnCompiledAndLinked(); + VerifyGLState(); + return true; + + error: + m_ok = false; + Free(); + return false; +} + +bool CARBShaderProgram::Enable() +{ + if (OK()) + { + if (m_pFP->OK()) + { + glEnable(GL_FRAGMENT_PROGRAM_ARB); + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_pFP->Handle()); + } + if (m_pVP->OK()) + { + glEnable(GL_VERTEX_PROGRAM_ARB); + glBindProgramARB(GL_VERTEX_PROGRAM_ARB, m_pVP->Handle()); + } + if (OnEnabled()) + { + VerifyGLState(); + return true; + } + else + { + glDisable(GL_FRAGMENT_PROGRAM_ARB); + glDisable(GL_VERTEX_PROGRAM_ARB); + return false; + } + } + return false; +} + +void CARBShaderProgram::Disable() +{ + if (OK()) + { + glDisable(GL_FRAGMENT_PROGRAM_ARB); + glDisable(GL_VERTEX_PROGRAM_ARB); + OnDisabled(); + } +} + +#endif + +#endif diff --git a/guilib/Shader.h b/guilib/Shader.h new file mode 100644 index 0000000000..b9d6775dc8 --- /dev/null +++ b/guilib/Shader.h @@ -0,0 +1,263 @@ +#ifndef __SHADER_H__ +#define __SHADER_H__ + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" + +#include <vector> +#include <string> + +#if defined(HAS_GL) || defined(HAS_GLES) + +namespace Shaders { + + using namespace std; + + ////////////////////////////////////////////////////////////////////// + // CShader - base class + ////////////////////////////////////////////////////////////////////// + class CShader + { + public: + CShader() { m_compiled = false; } + virtual ~CShader() { } + virtual bool Compile() = 0; + virtual void Free() = 0; + virtual GLuint Handle() = 0; + virtual void SetSource(const string& src) { m_source = src; } + virtual bool LoadSource(const string& filename, const string& prefix = ""); + bool OK() { return m_compiled; } + + protected: + string m_source; + string m_lastLog; + vector<string> m_attr; + bool m_compiled; + + }; + + + ////////////////////////////////////////////////////////////////////// + // CVertexShader - vertex shader class + ////////////////////////////////////////////////////////////////////// + class CVertexShader : public CShader + { + public: + CVertexShader() { m_vertexShader = 0; } + virtual ~CVertexShader() { Free(); } + virtual void Free() {} + virtual GLuint Handle() { return m_vertexShader; } + + protected: + GLuint m_vertexShader; + }; + + class CGLSLVertexShader : public CVertexShader + { + public: + virtual void Free(); + virtual bool Compile(); + }; + +#ifndef HAS_GLES + class CARBVertexShader : public CVertexShader + { + public: + virtual void Free(); + virtual bool Compile(); + }; +#endif + + + ////////////////////////////////////////////////////////////////////// + // CPixelShader - abstract pixel shader class + ////////////////////////////////////////////////////////////////////// + class CPixelShader : public CShader + { + public: + CPixelShader() { m_pixelShader = 0; } + virtual ~CPixelShader() { Free(); } + virtual void Free() {} + virtual GLuint Handle() { return m_pixelShader; } + + protected: + GLuint m_pixelShader; + }; + + + class CGLSLPixelShader : public CPixelShader + { + public: + virtual void Free(); + virtual bool Compile(); + }; + +#ifndef HAS_GLES + class CARBPixelShader : public CPixelShader + { + public: + virtual void Free(); + virtual bool Compile(); + }; +#endif + + ////////////////////////////////////////////////////////////////////// + // CShaderProgram - the complete shader consisting of both the vertex + // and pixel programs. (abstract) + ////////////////////////////////////////////////////////////////////// + class CShaderProgram + { + public: + CShaderProgram() + { + m_ok = false; + m_shaderProgram = 0; + m_pFP = NULL; + m_pVP = NULL; + } + + virtual ~CShaderProgram() + { + Free(); + delete m_pFP; + delete m_pVP; + } + + // enable the shader + virtual bool Enable() = 0; + + // disable the shader + virtual void Disable() = 0; + + // returns true if shader is compiled and linked + bool OK() { return m_ok; } + + // free resources + virtual void Free() {} + + // return the vertex shader object + CVertexShader* VertexShader() { return m_pVP; } + + // return the pixel shader object + CPixelShader* PixelShader() { return m_pFP; } + + // compile and link the shaders + virtual bool CompileAndLink() = 0; + + // override to to perform custom tasks on successfull compilation + // and linkage. E.g. obtaining handles to shader attributes. + virtual void OnCompiledAndLinked() {} + + // override to to perform custom tasks before shader is enabled + // and after it is disabled. Return false in OnDisabled() to + // disable shader. + // E.g. setting attributes, disabling texture unites, etc + virtual bool OnEnabled() { return true; } + virtual void OnDisabled() { } + + virtual GLuint ProgramHandle() { return m_shaderProgram; } + + protected: + CVertexShader* m_pVP; + CPixelShader* m_pFP; + GLuint m_shaderProgram; + bool m_ok; + }; + + + class CGLSLShaderProgram + : virtual public CShaderProgram + { + public: + CGLSLShaderProgram() + { + m_pFP = new CGLSLPixelShader(); + m_pVP = new CGLSLVertexShader(); + } + CGLSLShaderProgram(const std::string& vert + , const std::string& frag) + { + m_pFP = new CGLSLPixelShader(); + m_pFP->LoadSource(vert); + m_pVP = new CGLSLVertexShader(); + m_pVP->LoadSource(vert); + } + + // enable the shader + virtual bool Enable(); + + // disable the shader + virtual void Disable(); + + // free resources + virtual void Free(); + + // compile and link the shaders + virtual bool CompileAndLink(); + + protected: + GLint m_lastProgram; + }; + + +#ifndef HAS_GLES + class CARBShaderProgram + : virtual public CShaderProgram + { + public: + CARBShaderProgram() + { + m_pFP = new CARBPixelShader(); + m_pVP = new CARBVertexShader(); + } + CARBShaderProgram(const std::string& vert + , const std::string& frag) + { + m_pFP = new CARBPixelShader(); + m_pFP->LoadSource(vert); + m_pVP = new CARBVertexShader(); + m_pVP->LoadSource(vert); + } + + // enable the shader + virtual bool Enable(); + + // disable the shader + virtual void Disable(); + + // free resources + virtual void Free(); + + // compile and link the shaders + virtual bool CompileAndLink(); + + protected: + + }; +#endif + +} // close namespace + +#endif + +#endif //__SHADER_H__ diff --git a/guilib/SkinInfo.cpp b/guilib/SkinInfo.cpp new file mode 100644 index 0000000000..8b33ac0159 --- /dev/null +++ b/guilib/SkinInfo.cpp @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "SkinInfo.h" +#include "GUIWindowManager.h" +#include "GUISettings.h" +#include "FileSystem/File.h" +#include "FileSystem/SpecialProtocol.h" +#include "Key.h" +#include "Util.h" +#include "Settings.h" +#include "StringUtils.h" +#include "utils/log.h" +#include "tinyXML/tinyxml.h" + +using namespace std; +using namespace XFILE; + +#define SKIN_MIN_VERSION 2.1f + +CSkinInfo g_SkinInfo; // global + +CSkinInfo::CSkinInfo() +{ + m_DefaultResolution = RES_INVALID; + m_DefaultResolutionWide = RES_INVALID; + m_strBaseDir = ""; + m_iNumCreditLines = 0; + m_effectsSlowDown = 1.0; + m_onlyAnimateToHome = true; + m_Version = 1.0; + m_skinzoom = 1.0f; +} + +CSkinInfo::~CSkinInfo() +{} + +void CSkinInfo::Load(const CStdString& strSkinDir) +{ + m_strBaseDir = strSkinDir; + m_DefaultResolution = RES_INVALID; // set to INVALID to denote that there is no default res here + m_DefaultResolutionWide = RES_INVALID; + m_effectsSlowDown = 1.0; + // Load from skin.xml + TiXmlDocument xmlDoc; + CStdString strFile = m_strBaseDir + "\\skin.xml"; + if (xmlDoc.LoadFile(strFile)) + { // ok - get the default skin folder out of it... + TiXmlElement* pRootElement = xmlDoc.RootElement(); + CStdString strValue = pRootElement->Value(); + if (strValue != "skin") + { + CLog::Log(LOGERROR, "file :%s doesnt contain <skin>", strFile.c_str()); + } + else + { // get the default resolution + TiXmlNode *pChild = pRootElement->FirstChild("defaultresolution"); + if (pChild) + { // found the defaultresolution tag + CStdString strDefaultDir = pChild->FirstChild()->Value(); + strDefaultDir = strDefaultDir.ToLower(); + if (strDefaultDir == "pal") m_DefaultResolution = RES_PAL_4x3; + else if (strDefaultDir == "pal16x9") m_DefaultResolution = RES_PAL_16x9; + else if (strDefaultDir == "ntsc") m_DefaultResolution = RES_NTSC_4x3; + else if (strDefaultDir == "ntsc16x9") m_DefaultResolution = RES_NTSC_16x9; + else if (strDefaultDir == "720p") m_DefaultResolution = RES_HDTV_720p; + else if (strDefaultDir == "1080i") m_DefaultResolution = RES_HDTV_1080i; + } + CLog::Log(LOGINFO, "Default 4:3 resolution directory is %s", CUtil::AddFileToFolder(m_strBaseDir, GetDirFromRes(m_DefaultResolution)).c_str()); + + pChild = pRootElement->FirstChild("defaultresolutionwide"); + if (pChild && pChild->FirstChild()) + { // found the defaultresolution tag + CStdString strDefaultDir = pChild->FirstChild()->Value(); + strDefaultDir = strDefaultDir.ToLower(); + if (strDefaultDir == "pal") m_DefaultResolutionWide = RES_PAL_4x3; + else if (strDefaultDir == "pal16x9") m_DefaultResolutionWide = RES_PAL_16x9; + else if (strDefaultDir == "ntsc") m_DefaultResolutionWide = RES_NTSC_4x3; + else if (strDefaultDir == "ntsc16x9") m_DefaultResolutionWide = RES_NTSC_16x9; + else if (strDefaultDir == "720p") m_DefaultResolutionWide = RES_HDTV_720p; + else if (strDefaultDir == "1080i") m_DefaultResolutionWide = RES_HDTV_1080i; + } + else + m_DefaultResolutionWide = m_DefaultResolution; // default to same as 4:3 + CLog::Log(LOGINFO, "Default 16:9 resolution directory is %s", CUtil::AddFileToFolder(m_strBaseDir, GetDirFromRes(m_DefaultResolutionWide)).c_str()); + + // get the version + pChild = pRootElement->FirstChild("version"); + if (pChild && pChild->FirstChild()) + { + m_Version = StringUtils::GetFloat(pChild->FirstChild()->Value()); + CLog::Log(LOGINFO, "Skin version is: %s", pChild->FirstChild()->Value()); + } + + // get the effects slowdown parameter + pChild = pRootElement->FirstChild("effectslowdown"); + if (pChild && pChild->FirstChild()) + m_effectsSlowDown = StringUtils::GetFloat(pChild->FirstChild()->Value()); + + // now load the credits information + pChild = pRootElement->FirstChild("credits"); + if (pChild) + { // ok, run through the credits + TiXmlNode *pGrandChild = pChild->FirstChild("skinname"); + if (pGrandChild && pGrandChild->FirstChild()) + { + CStdString strName = pGrandChild->FirstChild()->Value(); +#ifndef _LINUX + swprintf(credits[0], L"%S Skin", strName.Left(44).c_str()); +#else + swprintf(credits[0], CREDIT_LINE_LENGTH - 1, L"%s Skin", strName.Left(44).c_str()); +#endif + } + pGrandChild = pChild->FirstChild("name"); + m_iNumCreditLines = 1; + while (pGrandChild && pGrandChild->FirstChild() && m_iNumCreditLines < 6) + { + CStdString strName = pGrandChild->FirstChild()->Value(); +#ifndef _LINUX + swprintf(credits[m_iNumCreditLines], L"%S", strName.Left(49).c_str()); +#else + swprintf(credits[m_iNumCreditLines], CREDIT_LINE_LENGTH - 1, L"%s", strName.Left(49).c_str()); +#endif + m_iNumCreditLines++; + pGrandChild = pGrandChild->NextSibling("name"); + } + } + + // get the skin zoom parameter. it's how much skin should be enlarged to get rid of overscan + pChild = pRootElement->FirstChild("zoom"); + if (pChild && pChild->FirstChild()) + m_skinzoom = StringUtils::GetFloat(pChild->FirstChild()->Value()); + else + m_skinzoom = 1.0f; + + // now load the startupwindow information + LoadStartupWindows(pRootElement->FirstChildElement("startupwindows")); + } + } + // Load the skin includes + LoadIncludes(); +} + +bool CSkinInfo::Check(const CStdString& strSkinDir) +{ + bool bVersionOK = false; + // Load from skin.xml + TiXmlDocument xmlDoc; + CStdString strFile = CUtil::AddFileToFolder(strSkinDir, "skin.xml"); + CStdString strGoodPath = strSkinDir; + if (xmlDoc.LoadFile(strFile)) + { // ok - get the default res folder out of it... + TiXmlElement* pRootElement = xmlDoc.RootElement(); + CStdString strValue = pRootElement->Value(); + if (strValue == "skin") + { // get the default resolution + TiXmlNode *pChild = pRootElement->FirstChild("defaultresolution"); + if (pChild) + { // found the defaultresolution tag +#ifndef _LINUX + strGoodPath += "\\"; +#else + strGoodPath += "/"; +#endif + CStdString resolution = pChild->FirstChild()->Value(); + if (resolution == "pal") resolution = "PAL"; + else if (resolution == "pal16x9") resolution = "PAL16x9"; + else if (resolution == "ntsc") resolution = "NTSC"; + else if (resolution == "ntsc16x9") resolution = "NTSC16x9"; + strGoodPath = CUtil::AddFileToFolder(strGoodPath, resolution); + } + // get the version + pChild = pRootElement->FirstChild("version"); + if (pChild) + { + float parsedVersion; + parsedVersion = StringUtils::GetFloat(pChild->FirstChild()->Value()); + bVersionOK = parsedVersion >= SKIN_MIN_VERSION; + + CLog::Log(LOGINFO, "Skin version is: %s (%f)", pChild->FirstChild()->Value(), parsedVersion); + } + } + } + // Check to see if we have a good path + CStdString strFontXML = CUtil::AddFileToFolder(strGoodPath, "Font.xml"); + CStdString strHomeXML = CUtil::AddFileToFolder(strGoodPath, "Home.xml"); + if ( CFile::Exists(strFontXML) && + CFile::Exists(strHomeXML) && bVersionOK ) + { + return true; + } + return false; +} + +CStdString CSkinInfo::GetSkinPath(const CStdString& strFile, RESOLUTION *res, const CStdString& strBaseDir /* = "" */) +{ + CStdString strPathToUse = m_strBaseDir; + if (!strBaseDir.IsEmpty()) + strPathToUse = strBaseDir; + // first try and load from the current resolution's directory + int height=0; + *res = g_graphicsContext.GetVideoResolution(); + if (*res >= RES_WINDOW) + { + height = g_settings.m_ResInfo[*res].iHeight; + if (height>=1080 && (g_settings.m_ResInfo[*res].dwFlags & D3DPRESENTFLAG_WIDESCREEN)) + { + *res = RES_HDTV_1080i; + } + else if (height>=720 && (g_settings.m_ResInfo[*res].dwFlags & D3DPRESENTFLAG_WIDESCREEN)) + { + *res = RES_HDTV_720p; + } + else if (g_settings.m_ResInfo[*res].dwFlags & D3DPRESENTFLAG_WIDESCREEN) + { + *res = RES_PAL_16x9; + } + else + { + *res = RES_PAL_4x3; + } + } + CStdString strPath = CUtil::AddFileToFolder(strPathToUse, GetDirFromRes(*res)); + strPath = CUtil::AddFileToFolder(strPath, strFile); + if (CFile::Exists(strPath)) + return strPath; + // if we're in 1080i mode, try 720p next + if (*res == RES_HDTV_1080i) + { + *res = RES_HDTV_720p; + strPath = CUtil::AddFileToFolder(strPathToUse, GetDirFromRes(*res)); + strPath = CUtil::AddFileToFolder(strPath, strFile); + if (CFile::Exists(strPath)) + return strPath; + } + // that failed - drop to the default widescreen resolution if where in a widemode + if (*res == RES_PAL_16x9 || *res == RES_NTSC_16x9 || *res == RES_HDTV_480p_16x9 || *res == RES_HDTV_720p) + { + *res = m_DefaultResolutionWide; + strPath = CUtil::AddFileToFolder(strPathToUse, GetDirFromRes(*res)); + strPath = CUtil::AddFileToFolder(strPath, strFile); + if (CFile::Exists(strPath)) + return strPath; + } + // that failed - drop to the default resolution + *res = m_DefaultResolution; + strPath = CUtil::AddFileToFolder(strPathToUse, GetDirFromRes(*res)); + strPath = CUtil::AddFileToFolder(strPath, strFile); + // check if we don't have any subdirectories + if (*res == RES_INVALID) *res = RES_PAL_4x3; + return strPath; +} + +bool CSkinInfo::HasSkinFile(const CStdString &strFile) +{ + RESOLUTION res = RES_INVALID; + return CFile::Exists(GetSkinPath(strFile, &res)); +} + +CStdString CSkinInfo::GetDirFromRes(RESOLUTION res) +{ + CStdString strRes; + switch (res) + { + case RES_PAL_4x3: + strRes = "PAL"; + break; + case RES_PAL_16x9: + strRes = "PAL16x9"; + break; + case RES_NTSC_4x3: + case RES_HDTV_480p_4x3: + strRes = "NTSC"; + break; + case RES_NTSC_16x9: + case RES_HDTV_480p_16x9: + strRes = "ntsc16x9"; + break; + case RES_HDTV_720p: + strRes = "720p"; + break; + case RES_HDTV_1080i: + strRes = "1080i"; + break; + case RES_INVALID: + default: + strRes = ""; + break; + } + return strRes; +} + +CStdString CSkinInfo::GetBaseDir() +{ + return m_strBaseDir; +} + +wchar_t* CSkinInfo::GetCreditsLine(int i) +{ + if (i < m_iNumCreditLines) + return credits[i]; + else + return NULL; +} + +double CSkinInfo::GetMinVersion() +{ + return SKIN_MIN_VERSION; +} + +void CSkinInfo::LoadIncludes() +{ + RESOLUTION res; + CStdString includesPath = PTH_IC(GetSkinPath("includes.xml", &res)); + CLog::Log(LOGINFO, "Loading skin includes from %s", includesPath.c_str()); + m_includes.ClearIncludes(); + m_includes.LoadIncludes(includesPath); +} + +void CSkinInfo::ResolveIncludes(TiXmlElement *node, const CStdString &type) +{ + m_includes.ResolveIncludes(node, type); +} + +bool CSkinInfo::ResolveConstant(const CStdString &constant, float &value) +{ + return m_includes.ResolveConstant(constant, value); +} + +bool CSkinInfo::ResolveConstant(const CStdString &constant, DWORD &value) +{ + float fValue; + if (m_includes.ResolveConstant(constant, fValue)) + { + value = (DWORD)fValue; + return true; + } + return false; +} + +int CSkinInfo::GetStartWindow() +{ + int windowID = g_guiSettings.GetInt("lookandfeel.startupwindow"); + assert(m_startupWindows.size()); + for (vector<CStartupWindow>::iterator it = m_startupWindows.begin(); it != m_startupWindows.end(); it++) + { + if (windowID == (*it).m_id) + return windowID; + } + // return our first one + return m_startupWindows[0].m_id; +} + +bool CSkinInfo::LoadStartupWindows(const TiXmlElement *startup) +{ + m_startupWindows.clear(); + if (startup) + { // yay, run through and grab the startup windows + const TiXmlElement *window = startup->FirstChildElement("window"); + while (window && window->FirstChild()) + { + int id; + window->Attribute("id", &id); + CStdString name = window->FirstChild()->Value(); + m_startupWindows.push_back(CStartupWindow(id + WINDOW_HOME, name)); + window = window->NextSiblingElement("window"); + } + } + + // ok, now see if we have any startup windows + if (!m_startupWindows.size()) + { // nope - add the default ones + m_startupWindows.push_back(CStartupWindow(WINDOW_HOME, "513")); + m_startupWindows.push_back(CStartupWindow(WINDOW_PROGRAMS, "0")); + m_startupWindows.push_back(CStartupWindow(WINDOW_PICTURES, "1")); + m_startupWindows.push_back(CStartupWindow(WINDOW_MUSIC, "2")); + m_startupWindows.push_back(CStartupWindow(WINDOW_VIDEOS, "3")); + m_startupWindows.push_back(CStartupWindow(WINDOW_FILES, "7")); + m_startupWindows.push_back(CStartupWindow(WINDOW_SETTINGS_MENU, "5")); + m_startupWindows.push_back(CStartupWindow(WINDOW_SCRIPTS, "247")); + m_onlyAnimateToHome = true; + } + else + m_onlyAnimateToHome = false; + return true; +} +void CSkinInfo::SetDefaults() +{ + m_DefaultResolution = RES_PAL_4x3; + m_DefaultResolutionWide = RES_PAL_16x9; +} diff --git a/guilib/SkinInfo.h b/guilib/SkinInfo.h new file mode 100644 index 0000000000..38dec2bccf --- /dev/null +++ b/guilib/SkinInfo.h @@ -0,0 +1,95 @@ +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GraphicContext.h" // needed for the RESOLUTION members +#include "GUIIncludes.h" // needed for the GUIInclude member + +#define CREDIT_LINE_LENGTH 50 + +class CSkinInfo +{ +public: + class CStartupWindow + { + public: + CStartupWindow(int id, const CStdString &name) + { + m_id = id; m_name = name; + }; + int m_id; + CStdString m_name; + }; + + CSkinInfo(); + ~CSkinInfo(); + + void Load(const CStdString& strSkinDir); // load the skin.xml file if it exists, and configure our directories etc. + bool Check(const CStdString& strSkinDir); // checks if everything is present and accounted for without loading the skin + + bool HasSkinFile(const CStdString &strFile); + CStdString GetSkinPath(const CStdString& strFile, RESOLUTION *res, const CStdString& strBaseDir=""); // retrieve the best skin file for the resolution we are in - res will be made the resolution we are loading from + wchar_t* GetCreditsLine(int i); + + CStdString GetDirFromRes(RESOLUTION res); + CStdString GetBaseDir(); + double GetMinVersion(); + double GetVersion(){ return m_Version;}; + int GetStartWindow(); + + void ResolveIncludes(TiXmlElement *node, const CStdString &type = ""); + bool ResolveConstant(const CStdString &constant, float &value); + bool ResolveConstant(const CStdString &constant, DWORD &value); + + double GetEffectsSlowdown() const { return m_effectsSlowDown; }; + + const std::vector<CStartupWindow> &GetStartupWindows() { return m_startupWindows; }; + + bool OnlyAnimateToHome() { return m_onlyAnimateToHome; }; + + inline float GetSkinZoom() { return m_skinzoom; }; + + inline const RESOLUTION& GetDefaultWideResolution() { return m_DefaultResolutionWide; }; + inline const RESOLUTION& GetDefaultResolution() { return m_DefaultResolution; }; + + void SetDefaults(); +protected: + void LoadIncludes(); + bool LoadStartupWindows(const TiXmlElement *startup); + + wchar_t credits[6][CREDIT_LINE_LENGTH]; // credits info + int m_iNumCreditLines; // number of credit lines + RESOLUTION m_DefaultResolution; // default resolution for the skin in 4:3 modes + RESOLUTION m_DefaultResolutionWide; // default resolution for the skin in 16:9 modes + CStdString m_strBaseDir; + double m_Version; + + double m_effectsSlowDown; + CGUIIncludes m_includes; + + std::vector<CStartupWindow> m_startupWindows; + bool m_onlyAnimateToHome; + + float m_skinzoom; +}; + +extern CSkinInfo g_SkinInfo; diff --git a/guilib/StdString.h b/guilib/StdString.h new file mode 100644 index 0000000000..ab64f7a6ea --- /dev/null +++ b/guilib/StdString.h @@ -0,0 +1,4335 @@ +#pragma once +#include <string> +#include <stdint.h> +#include <vector> +#if !defined(_LINUX) +#include "win32/PlatformDefs.h" // for va_copy +#endif + +// ============================================================================= +// FILE: StdString.h +// AUTHOR: Joe O'Leary (with outside help noted in comments) +// +// If you find any bugs in this code, please let me know: +// +// jmoleary@earthlink.net +// http://www.joeo.net/stdstring.htm (a bit outdated) +// +// The latest version of this code should always be available at the +// following link: +// +// http://www.joeo.net/code/StdString.zip (Dec 6, 2003) +// +// +// REMARKS: +// This header file declares the CStdStr template. This template derives +// the Standard C++ Library basic_string<> template and add to it the +// the following conveniences: +// - The full MFC CString set of functions (including implicit cast) +// - writing to/reading from COM IStream interfaces +// - Functional objects for use in STL algorithms +// +// From this template, we intstantiate two classes: CStdStringA and +// CStdStringW. The name "CStdString" is just a #define of one of these, +// based upone the UNICODE macro setting +// +// This header also declares our own version of the MFC/ATL UNICODE-MBCS +// conversion macros. Our version looks exactly like the Microsoft's to +// facilitate portability. +// +// NOTE: +// If you you use this in an MFC or ATL build, you should include either +// afx.h or atlbase.h first, as appropriate. +// +// PEOPLE WHO HAVE CONTRIBUTED TO THIS CLASS: +// +// Several people have helped me iron out problems and othewise improve +// this class. OK, this is a long list but in my own defense, this code +// has undergone two major rewrites. Many of the improvements became +// necessary after I rewrote the code as a template. Others helped me +// improve the CString facade. +// +// Anyway, these people are (in chronological order): +// +// - Pete the Plumber (???) +// - Julian Selman +// - Chris (of Melbsys) +// - Dave Plummer +// - John C Sipos +// - Chris Sells +// - Nigel Nunn +// - Fan Xia +// - Matthew Williams +// - Carl Engman +// - Mark Zeren +// - Craig Watson +// - Rich Zuris +// - Karim Ratib +// - Chris Conti +// - Baptiste Lepilleur +// - Greg Pickles +// - Jim Cline +// - Jeff Kohn +// - Todd Heckel +// - Ullrich Poll�hne +// - Joe Vitaterna +// - Joe Woodbury +// - Aaron (no last name) +// - Joldakowski (???) +// - Scott Hathaway +// - Eric Nitzche +// - Pablo Presedo +// - Farrokh Nejadlotfi +// - Jason Mills +// - Igor Kholodov +// - Mike Crusader +// - John James +// - Wang Haifeng +// - Tim Dowty +// - Arnt Witteveen +// - Glen Maynard +// - Paul DeMarco +// - Bagira (full name?) +// - Ronny Schulz +// - Jakko Van Hunen +// - Charles Godwin +// - Henk Demper +// - Greg Marr +// - Bill Carducci +// - Brian Groose +// - MKingman +// - Don Beusee +// +// REVISION HISTORY +// +// 2005-JAN-10 - Thanks to Don Beusee for pointing out the danger in mapping +// length-checked formatting functions to non-length-checked +// CRT equivalents. Also thanks to him for motivating me to +// optimize my implementation of Replace() +// +// 2004-APR-22 - A big, big thank you to "MKingman" (whoever you are) for +// finally spotting a silly little error in StdCodeCvt that +// has been causing me (and users of CStdString) problems for +// years in some relatively rare conversions. I had reversed +// two length arguments. +// +// 2003-NOV-24 - Thanks to a bunch of people for helping me clean up many +// compiler warnings (and yes, even a couple of actual compiler +// errors). These include Henk Demper for figuring out how +// to make the Intellisense work on with CStdString on VC6, +// something I was never able to do. Greg Marr pointed out +// a compiler warning about an unreferenced symbol and a +// problem with my version of Load in MFC builds. Bill +// Carducci took a lot of time with me to help me figure out +// why some implementations of the Standard C++ Library were +// returning error codes for apparently successful conversions +// between ASCII and UNICODE. Finally thanks to Brian Groose +// for helping me fix compiler signed unsigned warnings in +// several functions. +// +// 2003-JUL-10 - Thanks to Charles Godwin for making me realize my 'FmtArg' +// fixes had inadvertently broken the DLL-export code (which is +// normally commented out. I had to move it up higher. Also +// this helped me catch a bug in ssicoll that would prevent +// compilation, otherwise. +// +// 2003-MAR-14 - Thanks to Jakko Van Hunen for pointing out a copy-and-paste +// bug in one of the overloads of FmtArg. +// +// 2003-MAR-10 - Thanks to Ronny Schulz for (twice!) sending me some changes +// to help CStdString build on SGI and for pointing out an +// error in placement of my preprocessor macros for ssfmtmsg. +// +// 2002-NOV-26 - Thanks to Bagira for pointing out that my implementation of +// SpanExcluding was not properly handling the case in which +// the string did NOT contain any of the given characters +// +// 2002-OCT-21 - Many thanks to Paul DeMarco who was invaluable in helping me +// get this code working with Borland's free compiler as well +// as the Dev-C++ compiler (available free at SourceForge). +// +// 2002-SEP-13 - Thanks to Glen Maynard who helped me get rid of some loud +// but harmless warnings that were showing up on g++. Glen +// also pointed out that some pre-declarations of FmtArg<> +// specializations were unnecessary (and no good on G++) +// +// 2002-JUN-26 - Thanks to Arnt Witteveen for pointing out that I was using +// static_cast<> in a place in which I should have been using +// reinterpret_cast<> (the ctor for unsigned char strings). +// That's what happens when I don't unit-test properly! +// Arnt also noticed that CString was silently correcting the +// 'nCount' argument to Left() and Right() where CStdString was +// not (and crashing if it was bad). That is also now fixed! +// +// 2002-FEB-25 - Thanks to Tim Dowty for pointing out (and giving me the fix +// for) a conversion problem with non-ASCII MBCS characters. +// CStdString is now used in my favorite commercial MP3 player! +// +// 2001-DEC-06 - Thanks to Wang Haifeng for spotting a problem in one of the +// assignment operators (for _bstr_t) that would cause compiler +// errors when refcounting protection was turned off. +// +// 2001-NOV-27 - Remove calls to operator!= which involve reverse_iterators +// due to a conflict with the rel_ops operator!=. Thanks to +// John James for pointing this out. +// +// 2001-OCT-29 - Added a minor range checking fix for the Mid function to +// make it as forgiving as CString's version is. Thanks to +// Igor Kholodov for noticing this. +// - Added a specialization of std::swap for CStdString. Thanks +// to Mike Crusader for suggesting this! It's commented out +// because you're not supposed to inject your own code into the +// 'std' namespace. But if you don't care about that, it's +// there if you want it +// - Thanks to Jason Mills for catching a case where CString was +// more forgiving in the Delete() function than I was. +// +// 2001-JUN-06 - I was violating the Standard name lookup rules stated +// in [14.6.2(3)]. None of the compilers I've tried so +// far apparently caught this but HP-UX aCC 3.30 did. The +// fix was to add 'this->' prefixes in many places. +// Thanks to Farrokh Nejadlotfi for this! +// +// 2001-APR-27 - StreamLoad was calculating the number of BYTES in one +// case, not characters. Thanks to Pablo Presedo for this. +// +// 2001-FEB-23 - Replace() had a bug which caused infinite loops if the +// source string was empty. Fixed thanks to Eric Nitzsche. +// +// 2001-FEB-23 - Scott Hathaway was a huge help in providing me with the +// ability to build CStdString on Sun Unix systems. He +// sent me detailed build reports about what works and what +// does not. If CStdString compiles on your Unix box, you +// can thank Scott for it. +// +// 2000-DEC-29 - Joldakowski noticed one overload of Insert failed to do a +// range check as CString's does. Now fixed -- thanks! +// +// 2000-NOV-07 - Aaron pointed out that I was calling static member +// functions of char_traits via a temporary. This was not +// technically wrong, but it was unnecessary and caused +// problems for poor old buggy VC5. Thanks Aaron! +// +// 2000-JUL-11 - Joe Woodbury noted that the CString::Find docs don't match +// what the CString::Find code really ends up doing. I was +// trying to match the docs. Now I match the CString code +// - Joe also caught me truncating strings for GetBuffer() calls +// when the supplied length was less than the current length. +// +// 2000-MAY-25 - Better support for STLPORT's Standard library distribution +// - Got rid of the NSP macro - it interfered with Koenig lookup +// - Thanks to Joe Woodbury for catching a TrimLeft() bug that +// I introduced in January. Empty strings were not getting +// trimmed +// +// 2000-APR-17 - Thanks to Joe Vitaterna for pointing out that ReverseFind +// is supposed to be a const function. +// +// 2000-MAR-07 - Thanks to Ullrich Poll�hne for catching a range bug in one +// of the overloads of assign. +// +// 2000-FEB-01 - You can now use CStdString on the Mac with CodeWarrior! +// Thanks to Todd Heckel for helping out with this. +// +// 2000-JAN-23 - Thanks to Jim Cline for pointing out how I could make the +// Trim() function more efficient. +// - Thanks to Jeff Kohn for prompting me to find and fix a typo +// in one of the addition operators that takes _bstr_t. +// - Got rid of the .CPP file - you only need StdString.h now! +// +// 1999-DEC-22 - Thanks to Greg Pickles for helping me identify a problem +// with my implementation of CStdString::FormatV in which +// resulting string might not be properly NULL terminated. +// +// 1999-DEC-06 - Chris Conti pointed yet another basic_string<> assignment +// bug that MS has not fixed. CStdString did nothing to fix +// it either but it does now! The bug was: create a string +// longer than 31 characters, get a pointer to it (via c_str()) +// and then assign that pointer to the original string object. +// The resulting string would be empty. Not with CStdString! +// +// 1999-OCT-06 - BufferSet was erasing the string even when it was merely +// supposed to shrink it. Fixed. Thanks to Chris Conti. +// - Some of the Q172398 fixes were not checking for assignment- +// to-self. Fixed. Thanks to Baptiste Lepilleur. +// +// 1999-AUG-20 - Improved Load() function to be more efficient by using +// SizeOfResource(). Thanks to Rich Zuris for this. +// - Corrected resource ID constructor, again thanks to Rich. +// - Fixed a bug that occurred with UNICODE characters above +// the first 255 ANSI ones. Thanks to Craig Watson. +// - Added missing overloads of TrimLeft() and TrimRight(). +// Thanks to Karim Ratib for pointing them out +// +// 1999-JUL-21 - Made all calls to GetBuf() with no args check length first. +// +// 1999-JUL-10 - Improved MFC/ATL independence of conversion macros +// - Added SS_NO_REFCOUNT macro to allow you to disable any +// reference-counting your basic_string<> impl. may do. +// - Improved ReleaseBuffer() to be as forgiving as CString. +// Thanks for Fan Xia for helping me find this and to +// Matthew Williams for pointing it out directly. +// +// 1999-JUL-06 - Thanks to Nigel Nunn for catching a very sneaky bug in +// ToLower/ToUpper. They should call GetBuf() instead of +// data() in order to ensure the changed string buffer is not +// reference-counted (in those implementations that refcount). +// +// 1999-JUL-01 - Added a true CString facade. Now you can use CStdString as +// a drop-in replacement for CString. If you find this useful, +// you can thank Chris Sells for finally convincing me to give +// in and implement it. +// - Changed operators << and >> (for MFC CArchive) to serialize +// EXACTLY as CString's do. So now you can send a CString out +// to a CArchive and later read it in as a CStdString. I have +// no idea why you would want to do this but you can. +// +// 1999-JUN-21 - Changed the CStdString class into the CStdStr template. +// - Fixed FormatV() to correctly decrement the loop counter. +// This was harmless bug but a bug nevertheless. Thanks to +// Chris (of Melbsys) for pointing it out +// - Changed Format() to try a normal stack-based array before +// using to _alloca(). +// - Updated the text conversion macros to properly use code +// pages and to fit in better in MFC/ATL builds. In other +// words, I copied Microsoft's conversion stuff again. +// - Added equivalents of CString::GetBuffer, GetBufferSetLength +// - new sscpy() replacement of CStdString::CopyString() +// - a Trim() function that combines TrimRight() and TrimLeft(). +// +// 1999-MAR-13 - Corrected the "NotSpace" functional object to use _istpace() +// instead of _isspace() Thanks to Dave Plummer for this. +// +// 1999-FEB-26 - Removed errant line (left over from testing) that #defined +// _MFC_VER. Thanks to John C Sipos for noticing this. +// +// 1999-FEB-03 - Fixed a bug in a rarely-used overload of operator+() that +// caused infinite recursion and stack overflow +// - Added member functions to simplify the process of +// persisting CStdStrings to/from DCOM IStream interfaces +// - Added functional objects (e.g. StdStringLessNoCase) that +// allow CStdStrings to be used as keys STL map objects with +// case-insensitive comparison +// - Added array indexing operators (i.e. operator[]). I +// originally assumed that these were unnecessary and would be +// inherited from basic_string. However, without them, Visual +// C++ complains about ambiguous overloads when you try to use +// them. Thanks to Julian Selman to pointing this out. +// +// 1998-FEB-?? - Added overloads of assign() function to completely account +// for Q172398 bug. Thanks to "Pete the Plumber" for this +// +// 1998-FEB-?? - Initial submission +// +// COPYRIGHT: +// 2002 Joseph M. O'Leary. This code is 100% free. Use it anywhere you +// want. Rewrite it, restructure it, whatever. If you can write software +// that makes money off of it, good for you. I kinda like capitalism. +// Please don't blame me if it causes your $30 billion dollar satellite +// explode in orbit. If you redistribute it in any form, I'd appreciate it +// if you would leave this notice here. +// ============================================================================= + +// Avoid multiple inclusion + +#ifndef STDSTRING_H +#define STDSTRING_H + +// When using VC, turn off browser references +// Turn off unavoidable compiler warnings + +#if defined(_MSC_VER) && (_MSC_VER > 1100) + #pragma component(browser, off, references, "CStdString") + #pragma warning (disable : 4290) // C++ Exception Specification ignored + #pragma warning (disable : 4127) // Conditional expression is constant + #pragma warning (disable : 4097) // typedef name used as synonym for class name +#endif + +// Borland warnings to turn off + +#ifdef __BORLANDC__ + #pragma option push -w-inl +// #pragma warn -inl // Turn off inline function warnings +#endif + +// SS_IS_INTRESOURCE +// ----------------- +// A copy of IS_INTRESOURCE from VC7. Because old VC6 version of winuser.h +// doesn't have this. + +#define SS_IS_INTRESOURCE(_r) (false) + +#if !defined (SS_ANSI) && defined(_MSC_VER) + #undef SS_IS_INTRESOURCE + #if defined(_WIN64) + #define SS_IS_INTRESOURCE(_r) (((unsigned __int64)(_r) >> 16) == 0) + #else + #define SS_IS_INTRESOURCE(_r) (((unsigned long)(_r) >> 16) == 0) + #endif +#endif + + +// MACRO: SS_UNSIGNED +// ------------------ +// This macro causes the addition of a constructor and assignment operator +// which take unsigned characters. CString has such functions and in order +// to provide maximum CString-compatability, this code needs them as well. +// In practice you will likely never need these functions... + +//#define SS_UNSIGNED + +#ifdef SS_ALLOW_UNSIGNED_CHARS + #define SS_UNSIGNED +#endif + +// MACRO: SS_SAFE_FORMAT +// --------------------- +// This macro provides limited compatability with a questionable CString +// "feature". You can define it in order to avoid a common problem that +// people encounter when switching from CString to CStdString. +// +// To illustrate the problem -- With CString, you can do this: +// +// CString sName("Joe"); +// CString sTmp; +// sTmp.Format("My name is %s", sName); // WORKS! +// +// However if you were to try this with CStdString, your program would +// crash. +// +// CStdString sName("Joe"); +// CStdString sTmp; +// sTmp.Format("My name is %s", sName); // CRASHES! +// +// You must explicitly call c_str() or cast the object to the proper type +// +// sTmp.Format("My name is %s", sName.c_str()); // WORKS! +// sTmp.Format("My name is %s", static_cast<PCSTR>(sName));// WORKS! +// sTmp.Format("My name is %s", (PCSTR)sName); // WORKS! +// +// This is because it is illegal to pass anything but a POD type as a +// variadic argument to a variadic function (i.e. as one of the "..." +// arguments). The type const char* is a POD type. The type CStdString +// is not. Of course, neither is the type CString, but CString lets you do +// it anyway due to the way they laid out the class in binary. I have no +// control over this in CStdString since I derive from whatever +// implementation of basic_string is available. +// +// However if you have legacy code (which does this) that you want to take +// out of the MFC world and you don't want to rewrite all your calls to +// Format(), then you can define this flag and it will no longer crash. +// +// Note however that this ONLY works for Format(), not sprintf, fprintf, +// etc. If you pass a CStdString object to one of those functions, your +// program will crash. Not much I can do to get around this, short of +// writing substitutes for those functions as well. + +#define SS_SAFE_FORMAT // use new template style Format() function + + +// MACRO: SS_NO_IMPLICIT_CAST +// -------------------------- +// Some people don't like the implicit cast to const char* (or rather to +// const CT*) that CStdString (and MFC's CString) provide. That was the +// whole reason I created this class in the first place, but hey, whatever +// bakes your cake. Just #define this macro to get rid of the the implicit +// cast. + +//#define SS_NO_IMPLICIT_CAST // gets rid of operator const CT*() + + +// MACRO: SS_NO_REFCOUNT +// --------------------- +// turns off reference counting at the assignment level. Only needed +// for the version of basic_string<> that comes with Visual C++ versions +// 6.0 or earlier, and only then in some heavily multithreaded scenarios. +// Uncomment it if you feel you need it. + +//#define SS_NO_REFCOUNT + +// MACRO: SS_WIN32 +// --------------- +// When this flag is set, we are building code for the Win32 platform and +// may use Win32 specific functions (such as LoadString). This gives us +// a couple of nice extras for the code. +// +// Obviously, Microsoft's is not the only compiler available for Win32 out +// there. So I can't just check to see if _MSC_VER is defined to detect +// if I'm building on Win32. So for now, if you use MS Visual C++ or +// Borland's compiler, I turn this on. Otherwise you may turn it on +// yourself, if you prefer + +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(_WIN32) + #define SS_WIN32 +#endif + +// MACRO: SS_ANSI +// -------------- +// When this macro is defined, the code attempts only to use ANSI/ISO +// standard library functions to do it's work. It will NOT attempt to use +// any Win32 of Visual C++ specific functions -- even if they are +// available. You may define this flag yourself to prevent any Win32 +// of VC++ specific functions from being called. + +// If we're not on Win32, we MUST use an ANSI build + +#ifndef SS_WIN32 + #if !defined(SS_NO_ANSI) + #define SS_ANSI + #endif +#endif + +// MACRO: SS_ALLOCA +// ---------------- +// Some implementations of the Standard C Library have a non-standard +// function known as alloca(). This functions allows one to allocate a +// variable amount of memory on the stack. It is needed to implement +// the ASCII/MBCS conversion macros. +// +// I wanted to find some way to determine automatically if alloca() is +// available on this platform via compiler flags but that is asking for +// trouble. The crude test presented here will likely need fixing on +// other platforms. Therefore I'll leave it up to you to fiddle with +// this test to determine if it exists. Just make sure SS_ALLOCA is or +// is not defined as appropriate and you control this feature. + +#if defined(_MSC_VER) && !defined(SS_ANSI) + #define SS_ALLOCA +#endif + + +// MACRO: SS_MBCS +// -------------- +// Setting this macro means you are using MBCS characters. In MSVC builds, +// this macro gets set automatically by detection of the preprocessor flag +// _MBCS. For other platforms you may set it manually if you wish. The +// only effect it currently has is to cause the allocation of more space +// for wchar_t --> char conversions. +// Note that MBCS does not mean UNICODE. +// +// #define SS_MBCS +// + +#ifdef _MBCS + #define SS_MBCS +#endif + + +// MACRO SS_NO_LOCALE +// ------------------ +// If your implementation of the Standard C++ Library lacks the <locale> header, +// you can #define this macro to make your code build properly. Note that this +// is some of my newest code and frankly I'm not very sure of it, though it does +// pass my unit tests. + +// #define SS_NO_LOCALE + + +// Compiler Error regarding _UNICODE and UNICODE +// ----------------------------------------------- +// Microsoft header files are screwy. Sometimes they depend on a preprocessor +// flag named "_UNICODE". Other times they check "UNICODE" (note the lack of +// leading underscore in the second version". In several places, they silently +// "synchronize" these two flags this by defining one of the other was defined. +// In older version of this header, I used to try to do the same thing. +// +// However experience has taught me that this is a bad idea. You get weird +// compiler errors that seem to indicate things like LPWSTR and LPTSTR not being +// equivalent in UNICODE builds, stuff like that (when they MUST be in a proper +// UNICODE build). You end up scratching your head and saying, "But that HAS +// to compile!". +// +// So what should you do if you get this error? +// +// Make sure that both macros (_UNICODE and UNICODE) are defined before this +// file is included. You can do that by either +// +// a) defining both yourself before any files get included +// b) including the proper MS headers in the proper order +// c) including this file before any other file, uncommenting +// the #defines below, and commenting out the #errors +// +// Personally I recommend solution a) but it's your call. + +#ifdef _MSC_VER + #if defined (_UNICODE) && !defined (UNICODE) + #error UNICODE defined but not UNICODE + // #define UNICODE // no longer silently fix this + #endif + #if defined (UNICODE) && !defined (_UNICODE) + #error Warning, UNICODE defined but not _UNICODE + // #define _UNICODE // no longer silently fix this + #endif +#endif + + +// ----------------------------------------------------------------------------- +// MIN and MAX. The Standard C++ template versions go by so many names (at +// at least in the MS implementation) that you never know what's available +// ----------------------------------------------------------------------------- +template<class Type> +inline const Type& SSMIN(const Type& arg1, const Type& arg2) +{ + return arg2 < arg1 ? arg2 : arg1; +} +template<class Type> +inline const Type& SSMAX(const Type& arg1, const Type& arg2) +{ + return arg2 > arg1 ? arg2 : arg1; +} + +// If they have not #included W32Base.h (part of my W32 utility library) then +// we need to define some stuff. Otherwise, this is all defined there. + +#if !defined(W32BASE_H) + + // If they want us to use only standard C++ stuff (no Win32 stuff) + + #ifdef SS_ANSI + + // On Win32 we have TCHAR.H so just include it. This is NOT violating + // the spirit of SS_ANSI as we are not calling any Win32 functions here. + + #ifdef SS_WIN32 + + #include <TCHAR.H> + #include <WTYPES.H> + #ifndef STRICT + #define STRICT + #endif + + // ... but on non-Win32 platforms, we must #define the types we need. + + #else + + typedef const char* PCSTR; + typedef char* PSTR; + typedef const wchar_t* PCWSTR; + typedef wchar_t* PWSTR; + #ifdef UNICODE + typedef wchar_t TCHAR; + #else + typedef char TCHAR; + #endif + typedef wchar_t OLECHAR; + + #endif // #ifndef _WIN32 + + + // Make sure ASSERT and verify are defined using only ANSI stuff + + #ifndef ASSERT + #include <assert.h> + #define ASSERT(f) assert((f)) + #endif + #ifndef VERIFY + #ifdef _DEBUG + #define VERIFY(x) ASSERT((x)) + #else + #define VERIFY(x) x + #endif + #endif + + #else // ...else SS_ANSI is NOT defined + + #include <TCHAR.H> + #include <WTYPES.H> + #ifndef STRICT + #define STRICT + #endif + + // Make sure ASSERT and verify are defined + + #ifndef ASSERT + #include <crtdbg.h> + #define ASSERT(f) _ASSERTE((f)) + #endif + #ifndef VERIFY + #ifdef _DEBUG + #define VERIFY(x) ASSERT((x)) + #else + #define VERIFY(x) x + #endif + #endif + + #endif // #ifdef SS_ANSI + + #ifndef UNUSED + #define UNUSED(x) x + #endif + +#endif // #ifndef W32BASE_H + +// Standard headers needed + +#include <string> // basic_string +#include <algorithm> // for_each, etc. +#include <functional> // for StdStringLessNoCase, et al +#ifndef SS_NO_LOCALE + #include <locale> // for various facets +#endif + +// If this is a recent enough version of VC include comdef.h, so we can write +// member functions to deal with COM types & compiler support classes e.g. +// _bstr_t + +#if defined (_MSC_VER) && (_MSC_VER >= 1100) + #include <comdef.h> + #define SS_INC_COMDEF // signal that we #included MS comdef.h file + #define STDSTRING_INC_COMDEF + #define SS_NOTHROW __declspec(nothrow) +#else + #define SS_NOTHROW +#endif + +#ifndef TRACE + #define TRACE_DEFINED_HERE + #define TRACE +#endif + +// Microsoft defines PCSTR, PCWSTR, etc, but no PCTSTR. I hate to use the +// versions with the "L" in front of them because that's a leftover from Win 16 +// days, even though it evaluates to the same thing. Therefore, Define a PCSTR +// as an LPCTSTR. + +#if !defined(PCTSTR) && !defined(PCTSTR_DEFINED) + typedef const TCHAR* PCTSTR; + #define PCTSTR_DEFINED +#endif + +#if !defined(PCOLESTR) && !defined(PCOLESTR_DEFINED) + typedef const OLECHAR* PCOLESTR; + #define PCOLESTR_DEFINED +#endif + +#if !defined(POLESTR) && !defined(POLESTR_DEFINED) + typedef OLECHAR* POLESTR; + #define POLESTR_DEFINED +#endif + +#if !defined(PCUSTR) && !defined(PCUSTR_DEFINED) + typedef const unsigned char* PCUSTR; + typedef unsigned char* PUSTR; + #define PCUSTR_DEFINED +#endif + + +// SGI compiler 7.3 doesnt know these types - oh and btw, remember to use +// -LANG:std in the CXX Flags +#if defined(__sgi) + typedef unsigned long DWORD; + typedef void * LPCVOID; +#endif + + +// SS_USE_FACET macro and why we need it: +// +// Since I'm a good little Standard C++ programmer, I use locales. Thus, I +// need to make use of the use_facet<> template function here. Unfortunately, +// this need is complicated by the fact the MS' implementation of the Standard +// C++ Library has a non-standard version of use_facet that takes more +// arguments than the standard dictates. Since I'm trying to write CStdString +// to work with any version of the Standard library, this presents a problem. +// +// The upshot of this is that I can't do 'use_facet' directly. The MS' docs +// tell me that I have to use a macro, _USE() instead. Since _USE obviously +// won't be available in other implementations, this means that I have to write +// my OWN macro -- SS_USE_FACET -- that evaluates either to _USE or to the +// standard, use_facet. +// +// If you are having trouble with the SS_USE_FACET macro, in your implementation +// of the Standard C++ Library, you can define your own version of SS_USE_FACET. + +#ifndef schMSG + #define schSTR(x) #x + #define schSTR2(x) schSTR(x) + #define schMSG(desc) message(__FILE__ "(" schSTR2(__LINE__) "):" #desc) +#endif + +#ifndef SS_USE_FACET + + // STLPort #defines a macro (__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS) for + // all MSVC builds, erroneously in my opinion. It causes problems for + // my SS_ANSI builds. In my code, I always comment out that line. You'll + // find it in \stlport\config\stl_msvc.h + + #if defined(__SGI_STL_PORT) && (__SGI_STL_PORT >= 0x400 ) + + #if defined(__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS) && defined(_MSC_VER) + #ifdef SS_ANSI + #pragma schMSG(__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS defined!!) + #endif + #endif + #define SS_USE_FACET(loc, fac) std::use_facet<fac >(loc) + + #elif defined(_MSC_VER ) + + #define SS_USE_FACET(loc, fac) std::_USE(loc, fac) + + // ...and + #elif defined(_RWSTD_NO_TEMPLATE_ON_RETURN_TYPE) + + #define SS_USE_FACET(loc, fac) std::use_facet(loc, (fac*)0) + + #else + + #define SS_USE_FACET(loc, fac) std::use_facet<fac >(loc) + + #endif + +#endif + +// ============================================================================= +// UNICODE/MBCS conversion macros. Made to work just like the MFC/ATL ones. +// ============================================================================= + +#include <wchar.h> // Added to Std Library with Amendment #1. + +// First define the conversion helper functions. We define these regardless of +// any preprocessor macro settings since their names won't collide. + +// Not sure if we need all these headers. I believe ANSI says we do. + +#include <stdio.h> +#include <stdarg.h> +#include <wctype.h> +#include <ctype.h> +#include <stdlib.h> +#ifndef va_start + #include <varargs.h> +#endif + + +#ifdef SS_NO_LOCALE + + #if defined(_WIN32) || defined (_WIN32_WCE) + + inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCSTR pSrcA, int nSrc, + UINT acp=CP_ACP) + { + ASSERT(0 != pSrcA); + ASSERT(0 != pDstW); + pDstW[0] = '\0'; + MultiByteToWideChar(acp, 0, pSrcA, nSrc, pDstW, nDst); + return pDstW; + } + inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCUSTR pSrcA, int nSrc, + UINT acp=CP_ACP) + { + return StdCodeCvt(pDstW, nDst, (PCSTR)pSrcA, nSrc, acp); + } + + inline PSTR StdCodeCvt(PSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc, + UINT acp=CP_ACP) + { + ASSERT(0 != pDstA); + ASSERT(0 != pSrcW); + pDstA[0] = '\0'; + WideCharToMultiByte(acp, 0, pSrcW, nSrc, pDstA, nDst, 0, 0); + return pDstA; + } + inline PUSTR StdCodeCvt(PUSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc, + UINT acp=CP_ACP) + { + return (PUSTR)StdCodeCvt((PSTR)pDstA, nDst, pSrcW, nSrc, acp); + } + #else + #endif + +#else + + // StdCodeCvt - made to look like Win32 functions WideCharToMultiByte + // and MultiByteToWideChar but uses locales in SS_ANSI + // builds. There are a number of overloads. + // First argument is the destination buffer. + // Second argument is the source buffer + //#if defined (SS_ANSI) || !defined (SS_WIN32) + + // 'SSCodeCvt' - shorthand name for the codecvt facet we use + + typedef std::codecvt<wchar_t, char, mbstate_t> SSCodeCvt; + + inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCSTR pSrcA, int nSrc, + const std::locale& loc=std::locale()) + { + ASSERT(0 != pSrcA); + ASSERT(0 != pDstW); + + pDstW[0] = '\0'; + + if ( nSrc > 0 ) + { + PCSTR pNextSrcA = pSrcA; + PWSTR pNextDstW = pDstW; + SSCodeCvt::result res = SSCodeCvt::ok; + const SSCodeCvt& conv = SS_USE_FACET(loc, SSCodeCvt); + SSCodeCvt::state_type st= { 0 }; + res = conv.in(st, + pSrcA, pSrcA + nSrc, pNextSrcA, + pDstW, pDstW + nDst, pNextDstW); +#ifdef _LINUX +#define ASSERT2(a) if (!(a)) {fprintf(stderr, "StdString: Assertion Failed on line %d\n", __LINE__);} +#else +#define ASSERT2 ASSERT +#endif + ASSERT2(SSCodeCvt::ok == res); + ASSERT2(SSCodeCvt::error != res); + ASSERT2(pNextDstW >= pDstW); + ASSERT2(pNextSrcA >= pSrcA); +#undef ASSERT2 + // Null terminate the converted string + + if ( pNextDstW - pDstW > nDst ) + *(pDstW + nDst) = '\0'; + else + *pNextDstW = '\0'; + } + return pDstW; + } + inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCUSTR pSrcA, int nSrc, + const std::locale& loc=std::locale()) + { + return StdCodeCvt(pDstW, nDst, (PCSTR)pSrcA, nSrc, loc); + } + + inline PSTR StdCodeCvt(PSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc, + const std::locale& loc=std::locale()) + { + ASSERT(0 != pDstA); + ASSERT(0 != pSrcW); + + pDstA[0] = '\0'; + + if ( nSrc > 0 ) + { + PSTR pNextDstA = pDstA; + PCWSTR pNextSrcW = pSrcW; + SSCodeCvt::result res = SSCodeCvt::ok; + const SSCodeCvt& conv = SS_USE_FACET(loc, SSCodeCvt); + SSCodeCvt::state_type st= { 0 }; + res = conv.out(st, + pSrcW, pSrcW + nSrc, pNextSrcW, + pDstA, pDstA + nDst, pNextDstA); +#ifdef _LINUX +#define ASSERT2(a) if (!(a)) {fprintf(stderr, "StdString: Assertion Failed on line %d\n", __LINE__);} +#else +#define ASSERT2 ASSERT +#endif + ASSERT2(SSCodeCvt::error != res); + ASSERT2(SSCodeCvt::ok == res); // strict, comment out for sanity + ASSERT2(pNextDstA >= pDstA); + ASSERT2(pNextSrcW >= pSrcW); +#undef ASSERT2 + + // Null terminate the converted string + + if ( pNextDstA - pDstA > nDst ) + *(pDstA + nDst) = '\0'; + else + *pNextDstA = '\0'; + } + return pDstA; + } + + inline PUSTR StdCodeCvt(PUSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc, + const std::locale& loc=std::locale()) + { + return (PUSTR)StdCodeCvt((PSTR)pDstA, nDst, pSrcW, nSrc, loc); + } + +#endif + + + +// Unicode/MBCS conversion macros are only available on implementations of +// the "C" library that have the non-standard _alloca function. As far as I +// know that's only Microsoft's though I've heard that the function exists +// elsewhere. + +#if defined(SS_ALLOCA) && !defined SS_NO_CONVERSION + + #include <malloc.h> // needed for _alloca + + // Define our conversion macros to look exactly like Microsoft's to + // facilitate using this stuff both with and without MFC/ATL + + #ifdef _CONVERSION_USES_THREAD_LOCALE + + #ifndef _DEBUG + #define SSCVT int _cvt; _cvt; UINT _acp=GetACP(); \ + _acp; PCWSTR _pw; _pw; PCSTR _pa; _pa + #else + #define SSCVT int _cvt = 0; _cvt; UINT _acp=GetACP();\ + _acp; PCWSTR _pw=0; _pw; PCSTR _pa=0; _pa + #endif + #define SSA2W(pa) (\ + ((_pa = pa) == 0) ? 0 : (\ + _cvt = (sslen(_pa)),\ + StdCodeCvt((PWSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \ + _pa, _cvt, _acp))) + #define SSW2A(pw) (\ + ((_pw = pw) == 0) ? 0 : (\ + _cvt = sslen(_pw), \ + StdCodeCvt((LPSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \ + _pw, _cvt, _acp))) + #else + + #ifndef _DEBUG + #define SSCVT int _cvt; _cvt; UINT _acp=CP_ACP; _acp;\ + PCWSTR _pw; _pw; PCSTR _pa; _pa + #else + #define SSCVT int _cvt = 0; _cvt; UINT _acp=CP_ACP; \ + _acp; PCWSTR _pw=0; _pw; PCSTR _pa=0; _pa + #endif + #define SSA2W(pa) (\ + ((_pa = pa) == 0) ? 0 : (\ + _cvt = (sslen(_pa)),\ + StdCodeCvt((PWSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \ + _pa, _cvt))) + #define SSW2A(pw) (\ + ((_pw = pw) == 0) ? 0 : (\ + _cvt = (sslen(_pw)),\ + StdCodeCvt((LPSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \ + _pw, _cvt))) + #endif + + #define SSA2CW(pa) ((PCWSTR)SSA2W((pa))) + #define SSW2CA(pw) ((PCSTR)SSW2A((pw))) + + #ifdef UNICODE + #define SST2A SSW2A + #define SSA2T SSA2W + #define SST2CA SSW2CA + #define SSA2CT SSA2CW + // (Did you get a compiler error here about not being able to convert + // PTSTR into PWSTR? Then your _UNICODE and UNICODE flags are messed + // up. Best bet: #define BOTH macros before including any MS headers.) + inline PWSTR SST2W(PTSTR p) { return p; } + inline PTSTR SSW2T(PWSTR p) { return p; } + inline PCWSTR SST2CW(PCTSTR p) { return p; } + inline PCTSTR SSW2CT(PCWSTR p) { return p; } + #else + #define SST2W SSA2W + #define SSW2T SSW2A + #define SST2CW SSA2CW + #define SSW2CT SSW2CA + inline PSTR SST2A(PTSTR p) { return p; } + inline PTSTR SSA2T(PSTR p) { return p; } + inline PCSTR SST2CA(PCTSTR p) { return p; } + inline PCTSTR SSA2CT(PCSTR p) { return p; } + #endif // #ifdef UNICODE + + #if defined(UNICODE) + // in these cases the default (TCHAR) is the same as OLECHAR + inline PCOLESTR SST2COLE(PCTSTR p) { return p; } + inline PCTSTR SSOLE2CT(PCOLESTR p) { return p; } + inline POLESTR SST2OLE(PTSTR p) { return p; } + inline PTSTR SSOLE2T(POLESTR p) { return p; } + #elif defined(OLE2ANSI) + // in these cases the default (TCHAR) is the same as OLECHAR + inline PCOLESTR SST2COLE(PCTSTR p) { return p; } + inline PCTSTR SSOLE2CT(PCOLESTR p) { return p; } + inline POLESTR SST2OLE(PTSTR p) { return p; } + inline PTSTR SSOLE2T(POLESTR p) { return p; } + #else + //CharNextW doesn't work on Win95 so we use this + #define SST2COLE(pa) SSA2CW((pa)) + #define SST2OLE(pa) SSA2W((pa)) + #define SSOLE2CT(po) SSW2CA((po)) + #define SSOLE2T(po) SSW2A((po)) + #endif + + #ifdef OLE2ANSI + #define SSW2OLE SSW2A + #define SSOLE2W SSA2W + #define SSW2COLE SSW2CA + #define SSOLE2CW SSA2CW + inline POLESTR SSA2OLE(PSTR p) { return p; } + inline PSTR SSOLE2A(POLESTR p) { return p; } + inline PCOLESTR SSA2COLE(PCSTR p) { return p; } + inline PCSTR SSOLE2CA(PCOLESTR p){ return p; } + #else + #define SSA2OLE SSA2W + #define SSOLE2A SSW2A + #define SSA2COLE SSA2CW + #define SSOLE2CA SSW2CA + inline POLESTR SSW2OLE(PWSTR p) { return p; } + inline PWSTR SSOLE2W(POLESTR p) { return p; } + inline PCOLESTR SSW2COLE(PCWSTR p) { return p; } + inline PCWSTR SSOLE2CW(PCOLESTR p){ return p; } + #endif + + // Above we've defined macros that look like MS' but all have + // an 'SS' prefix. Now we need the real macros. We'll either + // get them from the macros above or from MFC/ATL. + + #if defined (USES_CONVERSION) + + #define _NO_STDCONVERSION // just to be consistent + + #else + + #ifdef _MFC_VER + + #include <afxconv.h> + #define _NO_STDCONVERSION // just to be consistent + + #else + + #define USES_CONVERSION SSCVT + #define A2CW SSA2CW + #define W2CA SSW2CA + #define T2A SST2A + #define A2T SSA2T + #define T2W SST2W + #define W2T SSW2T + #define T2CA SST2CA + #define A2CT SSA2CT + #define T2CW SST2CW + #define W2CT SSW2CT + #define ocslen sslen + #define ocscpy sscpy + #define T2COLE SST2COLE + #define OLE2CT SSOLE2CT + #define T2OLE SST2COLE + #define OLE2T SSOLE2CT + #define A2OLE SSA2OLE + #define OLE2A SSOLE2A + #define W2OLE SSW2OLE + #define OLE2W SSOLE2W + #define A2COLE SSA2COLE + #define OLE2CA SSOLE2CA + #define W2COLE SSW2COLE + #define OLE2CW SSOLE2CW + + #endif // #ifdef _MFC_VER + #endif // #ifndef USES_CONVERSION +#endif // #ifndef SS_NO_CONVERSION + +// Define ostring - generic name for std::basic_string<OLECHAR> + +#if !defined(ostring) && !defined(OSTRING_DEFINED) + typedef std::basic_string<OLECHAR> ostring; + #define OSTRING_DEFINED +#endif + +// StdCodeCvt when there's no conversion to be done +template <typename T> +inline T* StdCodeCvt(T* pDst, int nDst, const T* pSrc, int nSrc) +{ + int nChars = SSMIN(nSrc, nDst); + + if ( nChars > 0 ) + { + pDst[0] = '\0'; + std::basic_string<T>::traits_type::copy(pDst, pSrc, nChars); +// std::char_traits<T>::copy(pDst, pSrc, nChars); + pDst[nChars] = '\0'; + } + + return pDst; +} +inline PSTR StdCodeCvt(PSTR pDst, int nDst, PCUSTR pSrc, int nSrc) +{ + return StdCodeCvt(pDst, nDst, (PCSTR)pSrc, nSrc); +} +inline PUSTR StdCodeCvt(PUSTR pDst, int nDst, PCSTR pSrc, int nSrc) +{ + return (PUSTR)StdCodeCvt((PSTR)pDst, nDst, pSrc, nSrc); +} + +// Define tstring -- generic name for std::basic_string<TCHAR> + +#if !defined(tstring) && !defined(TSTRING_DEFINED) + typedef std::basic_string<TCHAR> tstring; + #define TSTRING_DEFINED +#endif + +// a very shorthand way of applying the fix for KB problem Q172398 +// (basic_string assignment bug) + +#if defined ( _MSC_VER ) && ( _MSC_VER < 1200 ) + #define Q172398(x) (x).erase() +#else + #define Q172398(x) +#endif + +// ============================================================================= +// INLINE FUNCTIONS ON WHICH CSTDSTRING RELIES +// +// Usually for generic text mapping, we rely on preprocessor macro definitions +// to map to string functions. However the CStdStr<> template cannot use +// macro-based generic text mappings because its character types do not get +// resolved until template processing which comes AFTER macro processing. In +// other words, the preprocessor macro UNICODE is of little help to us in the +// CStdStr template +// +// Therefore, to keep the CStdStr declaration simple, we have these inline +// functions. The template calls them often. Since they are inline (and NOT +// exported when this is built as a DLL), they will probably be resolved away +// to nothing. +// +// Without these functions, the CStdStr<> template would probably have to broken +// out into two, almost identical classes. Either that or it would be a huge, +// convoluted mess, with tons of "if" statements all over the place checking the +// size of template parameter CT. +// ============================================================================= + +#ifdef SS_NO_LOCALE + + // -------------------------------------------------------------------------- + // Win32 GetStringTypeEx wrappers + // -------------------------------------------------------------------------- + inline bool wsGetStringType(LCID lc, DWORD dwT, PCSTR pS, int nSize, + WORD* pWd) + { + return FALSE != GetStringTypeExA(lc, dwT, pS, nSize, pWd); + } + inline bool wsGetStringType(LCID lc, DWORD dwT, PCWSTR pS, int nSize, + WORD* pWd) + { + return FALSE != GetStringTypeExW(lc, dwT, pS, nSize, pWd); + } + + + template<typename CT> + inline bool ssisspace (CT t) + { + WORD toYourMother; + return wsGetStringType(GetThreadLocale(), CT_CTYPE1, &t, 1, &toYourMother) + && 0 != (C1_BLANK & toYourMother); + } + +#endif + +// If they defined SS_NO_REFCOUNT, then we must convert all assignments + +#if defined (_MSC_VER) && (_MSC_VER < 1300) + #ifdef SS_NO_REFCOUNT + #define SSREF(x) (x).c_str() + #else + #define SSREF(x) (x) + #endif +#else + #define SSREF(x) (x) +#endif + +// ----------------------------------------------------------------------------- +// sslen: strlen/wcslen wrappers +// ----------------------------------------------------------------------------- +template<typename CT> inline int sslen(const CT* pT) +{ + return 0 == pT ? 0 : (int)std::basic_string<CT>::traits_type::length(pT); +// return 0 == pT ? 0 : std::char_traits<CT>::length(pT); +} +inline SS_NOTHROW int sslen(const std::string& s) +{ + return static_cast<int>(s.length()); +} +inline SS_NOTHROW int sslen(const std::wstring& s) +{ + return static_cast<int>(s.length()); +} + +// ----------------------------------------------------------------------------- +// sstolower/sstoupper -- convert characters to upper/lower case +// ----------------------------------------------------------------------------- + +#ifdef SS_NO_LOCALE + inline char sstoupper(char ch) { return (char)::toupper(ch); } + inline wchar_t sstoupper(wchar_t ch){ return (wchar_t)::towupper(ch); } + inline char sstolower(char ch) { return (char)::tolower(ch); } + inline wchar_t sstolower(wchar_t ch){ return (wchar_t)::tolower(ch); } +#else + template<typename CT> + inline CT sstolower(const CT& t, const std::locale& loc = std::locale()) + { + return std::tolower<CT>(t, loc); + } + template<typename CT> + inline CT sstoupper(const CT& t, const std::locale& loc = std::locale()) + { + return std::toupper<CT>(t, loc); + } +#endif + +// ----------------------------------------------------------------------------- +// ssasn: assignment functions -- assign "sSrc" to "sDst" +// ----------------------------------------------------------------------------- +typedef std::string::size_type SS_SIZETYPE; // just for shorthand, really +typedef std::string::pointer SS_PTRTYPE; +typedef std::wstring::size_type SW_SIZETYPE; +typedef std::wstring::pointer SW_PTRTYPE; + + +template <typename T> +inline void ssasn(std::basic_string<T>& sDst, const std::basic_string<T>& sSrc) +{ + if ( sDst.c_str() != sSrc.c_str() ) + { + sDst.erase(); + sDst.assign(SSREF(sSrc)); + } +} +template <typename T> +inline void ssasn(std::basic_string<T>& sDst, const T *pA) +{ + // Watch out for NULLs, as always. + + if ( 0 == pA ) + { + sDst.erase(); + } + + // If pA actually points to part of sDst, we must NOT erase(), but + // rather take a substring + + else if ( pA >= sDst.c_str() && pA <= sDst.c_str() + sDst.size() ) + { + sDst =sDst.substr(static_cast<typename std::basic_string<T>::size_type>(pA-sDst.c_str())); + } + + // Otherwise (most cases) apply the assignment bug fix, if applicable + // and do the assignment + + else + { + Q172398(sDst); + sDst.assign(pA); + } +} +inline void ssasn(std::string& sDst, const std::wstring& sSrc) +{ + if ( sSrc.empty() ) + { + sDst.erase(); + } + else + { + int nDst = static_cast<int>(sSrc.size()); + + // In MBCS builds, pad the buffer to account for the possibility of + // some 3 byte characters. Not perfect but should get most cases. + +#ifdef SS_MBCS + // In MBCS builds, we don't know how long the destination string will be. + nDst = static_cast<int>(static_cast<double>(nDst) * 1.3); + sDst.resize(nDst+1); + PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst, + sSrc.c_str(), static_cast<int>(sSrc.size())); + sDst.resize(sslen(szCvt)); +#else + sDst.resize(nDst+1); + StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst, + sSrc.c_str(), static_cast<int>(sSrc.size())); + sDst.resize(sSrc.size()); +#endif + } +} +inline void ssasn(std::string& sDst, PCWSTR pW) +{ + int nSrc = sslen(pW); + if ( nSrc > 0 ) + { + int nSrc = sslen(pW); + int nDst = nSrc; + + // In MBCS builds, pad the buffer to account for the possibility of + // some 3 byte characters. Not perfect but should get most cases. + +#ifdef SS_MBCS + nDst = static_cast<int>(static_cast<double>(nDst) * 1.3); + // In MBCS builds, we don't know how long the destination string will be. + sDst.resize(nDst + 1); + PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst, + pW, nSrc); + sDst.resize(sslen(szCvt)); +#else + sDst.resize(nDst + 1); + StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst, pW, nSrc); + sDst.resize(nDst); +#endif + } + else + { + sDst.erase(); + } +} +inline void ssasn(std::string& sDst, const int nNull) +{ + //UNUSED(nNull); + ASSERT(nNull==0); + sDst.assign(""); +} +#undef StrSizeType +inline void ssasn(std::wstring& sDst, const std::string& sSrc) +{ + if ( sSrc.empty() ) + { + sDst.erase(); + } + else + { + int nSrc = static_cast<int>(sSrc.size()); + int nDst = nSrc; + + sDst.resize(nSrc+1); + PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()), nDst, + sSrc.c_str(), nSrc); + + sDst.resize(sslen(szCvt)); + } +} +inline void ssasn(std::wstring& sDst, PCSTR pA) +{ + int nSrc = sslen(pA); + + if ( 0 == nSrc ) + { + sDst.erase(); + } + else + { + int nDst = nSrc; + sDst.resize(nDst+1); + PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()), nDst, pA, + nSrc); + + sDst.resize(sslen(szCvt)); + } +} +inline void ssasn(std::wstring& sDst, const int nNull) +{ + //UNUSED(nNull); + ASSERT(nNull==0); + sDst.assign(L""); +} + +// ----------------------------------------------------------------------------- +// ssadd: string object concatenation -- add second argument to first +// ----------------------------------------------------------------------------- +inline void ssadd(std::string& sDst, const std::wstring& sSrc) +{ + int nSrc = static_cast<int>(sSrc.size()); + + if ( nSrc > 0 ) + { + int nDst = static_cast<int>(sDst.size()); + int nAdd = nSrc; + + // In MBCS builds, pad the buffer to account for the possibility of + // some 3 byte characters. Not perfect but should get most cases. + +#ifdef SS_MBCS + nAdd = static_cast<int>(static_cast<double>(nAdd) * 1.3); + sDst.resize(nDst+nAdd+1); + PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst), + nAdd, sSrc.c_str(), nSrc); + sDst.resize(nDst + sslen(szCvt)); +#else + sDst.resize(nDst+nAdd+1); + StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst), nAdd, sSrc.c_str(), nSrc); + sDst.resize(nDst + nAdd); +#endif + } +} +template <typename T> +inline void ssadd(typename std::basic_string<T>& sDst, const typename std::basic_string<T>& sSrc) +{ + sDst += sSrc; +} +inline void ssadd(std::string& sDst, PCWSTR pW) +{ + int nSrc = sslen(pW); + if ( nSrc > 0 ) + { + int nDst = static_cast<int>(sDst.size()); + int nAdd = nSrc; + +#ifdef SS_MBCS + nAdd = static_cast<int>(static_cast<double>(nAdd) * 1.3); + sDst.resize(nDst + nAdd + 1); + PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst), + nAdd, pW, nSrc); + sDst.resize(nDst + sslen(szCvt)); +#else + sDst.resize(nDst + nAdd + 1); + StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst), nAdd, pW, nSrc); + sDst.resize(nDst + nSrc); +#endif + } +} +template <typename T> +inline void ssadd(typename std::basic_string<T>& sDst, const T *pA) +{ + if ( pA ) + { + // If the string being added is our internal string or a part of our + // internal string, then we must NOT do any reallocation without + // first copying that string to another object (since we're using a + // direct pointer) + + if ( pA >= sDst.c_str() && pA <= sDst.c_str()+sDst.length()) + { + if ( sDst.capacity() <= sDst.size()+sslen(pA) ) + sDst.append(std::basic_string<T>(pA)); + else + sDst.append(pA); + } + else + { + sDst.append(pA); + } + } +} +inline void ssadd(std::wstring& sDst, const std::string& sSrc) +{ + if ( !sSrc.empty() ) + { + int nSrc = static_cast<int>(sSrc.size()); + int nDst = static_cast<int>(sDst.size()); + + sDst.resize(nDst + nSrc + 1); +#ifdef SS_MBCS + PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst), + nSrc, sSrc.c_str(), nSrc+1); + sDst.resize(nDst + sslen(szCvt)); +#else + StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst), nSrc, sSrc.c_str(), nSrc+1); + sDst.resize(nDst + nSrc); +#endif + } +} +inline void ssadd(std::wstring& sDst, PCSTR pA) +{ + int nSrc = sslen(pA); + + if ( nSrc > 0 ) + { + int nDst = static_cast<int>(sDst.size()); + + sDst.resize(nDst + nSrc + 1); +#ifdef SS_MBCS + PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst), + nSrc, pA, nSrc+1); + sDst.resize(nDst + sslen(szCvt)); +#else + StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst), nSrc, pA, nSrc+1); + sDst.resize(nDst + nSrc); +#endif + } +} + +// ----------------------------------------------------------------------------- +// sscmp: comparison (case sensitive, not affected by locale) +// ----------------------------------------------------------------------------- +template<typename CT> +inline int sscmp(const CT* pA1, const CT* pA2) +{ + CT f; + CT l; + + do + { + f = *(pA1++); + l = *(pA2++); + } while ( (f) && (f == l) ); + + return (int)(f - l); +} + +// ----------------------------------------------------------------------------- +// ssicmp: comparison (case INsensitive, not affected by locale) +// ----------------------------------------------------------------------------- +template<typename CT> +inline int ssicmp(const CT* pA1, const CT* pA2) +{ + // Using the "C" locale = "not affected by locale" + + std::locale loc = std::locale::classic(); + const std::ctype<CT>& ct = SS_USE_FACET(loc, std::ctype<CT>); + CT f; + CT l; + + do + { + f = ct.tolower(*(pA1++)); + l = ct.tolower(*(pA2++)); + } while ( (f) && (f == l) ); + + return (int)(f - l); +} + +// ----------------------------------------------------------------------------- +// ssupr/sslwr: Uppercase/Lowercase conversion functions +// ----------------------------------------------------------------------------- + +template<typename CT> +inline void sslwr(CT* pT, size_t nLen, const std::locale& loc=std::locale()) +{ + SS_USE_FACET(loc, std::ctype<CT>).tolower(pT, pT+nLen); +} +template<typename CT> +inline void ssupr(CT* pT, size_t nLen, const std::locale& loc=std::locale()) +{ + SS_USE_FACET(loc, std::ctype<CT>).toupper(pT, pT+nLen); +} + +// ----------------------------------------------------------------------------- +// vsprintf/vswprintf or _vsnprintf/_vsnwprintf equivalents. In standard +// builds we can't use _vsnprintf/_vsnwsprintf because they're MS extensions. +// +// ----------------------------------------------------------------------------- +// Borland's headers put some ANSI "C" functions in the 'std' namespace. +// Promote them to the global namespace so we can use them here. + +#if defined(__BORLANDC__) + using std::vsprintf; + using std::vswprintf; +#endif + + // GNU is supposed to have vsnprintf and vsnwprintf. But only the newer + // distributions do. + +#if defined(__GNUC__) + + inline int ssvsprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl) + { + return vsnprintf(pA, nCount, pFmtA, vl); + } + inline int ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl) + { + return vswprintf(pW, nCount, pFmtW, vl); + } + + // Microsofties can use +#elif defined(_MSC_VER) && !defined(SS_ANSI) + + inline int ssvsprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl) + { + return _vsnprintf(pA, nCount, pFmtA, vl); + } + inline int ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl) + { + return _vsnwprintf(pW, nCount, pFmtW, vl); + } + +#elif defined (SS_DANGEROUS_FORMAT) // ignore buffer size parameter if needed? + + inline int ssvsprintf(PSTR pA, size_t /*nCount*/, PCSTR pFmtA, va_list vl) + { + return vsprintf(pA, pFmtA, vl); + } + + inline int ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl) + { + // JMO: Some distributions of the "C" have a version of vswprintf that + // takes 3 arguments (e.g. Microsoft, Borland, GNU). Others have a + // version which takes 4 arguments (an extra "count" argument in the + // second position. The best stab I can take at this so far is that if + // you are NOT running with MS, Borland, or GNU, then I'll assume you + // have the version that takes 4 arguments. + // + // I'm sure that these checks don't catch every platform correctly so if + // you get compiler errors on one of the lines immediately below, it's + // probably because your implemntation takes a different number of + // arguments. You can comment out the offending line (and use the + // alternate version) or you can figure out what compiler flag to check + // and add that preprocessor check in. Regardless, if you get an error + // on these lines, I'd sure like to hear from you about it. + // + // Thanks to Ronny Schulz for the SGI-specific checks here. + +// #if !defined(__MWERKS__) && !defined(__SUNPRO_CC_COMPAT) && !defined(__SUNPRO_CC) + #if !defined(_MSC_VER) \ + && !defined (__BORLANDC__) \ + && !defined(__GNUC__) \ + && !defined(__sgi) + + return vswprintf(pW, nCount, pFmtW, vl); + + // suddenly with the current SGI 7.3 compiler there is no such function as + // vswprintf and the substitute needs explicit casts to compile + + #elif defined(__sgi) + + nCount; + return vsprintf( (char *)pW, (char *)pFmtW, vl); + + #else + + nCount; + return vswprintf(pW, pFmtW, vl); + + #endif + + } + +#endif + + // GOT COMPILER PROBLEMS HERE? + // --------------------------- + // Does your compiler choke on one or more of the following 2 functions? It + // probably means that you don't have have either vsnprintf or vsnwprintf in + // your version of the CRT. This is understandable since neither is an ANSI + // "C" function. However it still leaves you in a dilemma. In order to make + // this code build, you're going to have to to use some non-length-checked + // formatting functions that every CRT has: vsprintf and vswprintf. + // + // This is very dangerous. With the proper erroneous (or malicious) code, it + // can lead to buffer overlows and crashing your PC. Use at your own risk + // In order to use them, just #define SS_DANGEROUS_FORMAT at the top of + // this file. + // + // Even THEN you might not be all the way home due to some non-conforming + // distributions. More on this in the comments below. + + inline int ssnprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl) + { + #ifdef _MSC_VER + return _vsnprintf(pA, nCount, pFmtA, vl); + #else + return vsnprintf(pA, nCount, pFmtA, vl); + #endif + } + inline int ssnprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl) + { + #ifdef _MSC_VER + return _vsnwprintf(pW, nCount, pFmtW, vl); + #else + return vswprintf(pW, nCount, pFmtW, vl); + #endif + } + + + + +// ----------------------------------------------------------------------------- +// ssload: Type safe, overloaded ::LoadString wrappers +// There is no equivalent of these in non-Win32-specific builds. However, I'm +// thinking that with the message facet, there might eventually be one +// ----------------------------------------------------------------------------- +#if defined (SS_WIN32) && !defined(SS_ANSI) + inline int ssload(HMODULE hInst, UINT uId, PSTR pBuf, int nMax) + { + return ::LoadStringA(hInst, uId, pBuf, nMax); + } + inline int ssload(HMODULE hInst, UINT uId, PWSTR pBuf, int nMax) + { + return ::LoadStringW(hInst, uId, pBuf, nMax); + } +#if defined ( _MSC_VER ) && ( _MSC_VER >= 1500 ) + inline int ssload(HMODULE hInst, UINT uId, uint16_t *pBuf, int nMax) + { + return 0; + } + inline int ssload(HMODULE hInst, UINT uId, uint32_t *pBuf, int nMax) + { + return 0; + } +#endif +#endif + + +// ----------------------------------------------------------------------------- +// sscoll/ssicoll: Collation wrappers +// Note -- with MSVC I have reversed the arguments order here because the +// functions appear to return the opposite of what they should +// ----------------------------------------------------------------------------- +#ifndef SS_NO_LOCALE +template <typename CT> +inline int sscoll(const CT* sz1, int nLen1, const CT* sz2, int nLen2) +{ + const std::collate<CT>& coll = + SS_USE_FACET(std::locale(), std::collate<CT>); + + return coll.compare(sz2, sz2+nLen2, sz1, sz1+nLen1); +} +template <typename CT> +inline int ssicoll(const CT* sz1, int nLen1, const CT* sz2, int nLen2) +{ + const std::locale loc; + const std::collate<CT>& coll = SS_USE_FACET(loc, std::collate<CT>); + + // Some implementations seem to have trouble using the collate<> + // facet typedefs so we'll just default to basic_string and hope + // that's what the collate facet uses (which it generally should) + +// std::collate<CT>::string_type s1(sz1); +// std::collate<CT>::string_type s2(sz2); + const std::basic_string<CT> sEmpty; + std::basic_string<CT> s1(sz1 ? sz1 : sEmpty.c_str()); + std::basic_string<CT> s2(sz2 ? sz2 : sEmpty.c_str()); + + sslwr(const_cast<CT*>(s1.c_str()), nLen1, loc); + sslwr(const_cast<CT*>(s2.c_str()), nLen2, loc); + return coll.compare(s2.c_str(), s2.c_str()+nLen2, + s1.c_str(), s1.c_str()+nLen1); +} +#endif + + +// ----------------------------------------------------------------------------- +// ssfmtmsg: FormatMessage equivalents. Needed because I added a CString facade +// Again -- no equivalent of these on non-Win32 builds but their might one day +// be one if the message facet gets implemented +// ----------------------------------------------------------------------------- +#if defined (SS_WIN32) && !defined(SS_ANSI) + inline DWORD ssfmtmsg(DWORD dwFlags, LPCVOID pSrc, DWORD dwMsgId, + DWORD dwLangId, PSTR pBuf, DWORD nSize, + va_list* vlArgs) + { + return FormatMessageA(dwFlags, pSrc, dwMsgId, dwLangId, + pBuf, nSize,vlArgs); + } + inline DWORD ssfmtmsg(DWORD dwFlags, LPCVOID pSrc, DWORD dwMsgId, + DWORD dwLangId, PWSTR pBuf, DWORD nSize, + va_list* vlArgs) + { + return FormatMessageW(dwFlags, pSrc, dwMsgId, dwLangId, + pBuf, nSize,vlArgs); + } +#else +#endif + + + +// FUNCTION: sscpy. Copies up to 'nMax' characters from pSrc to pDst. +// ----------------------------------------------------------------------------- +// FUNCTION: sscpy +// inline int sscpy(PSTR pDst, PCSTR pSrc, int nMax=-1); +// inline int sscpy(PUSTR pDst, PCSTR pSrc, int nMax=-1) +// inline int sscpy(PSTR pDst, PCWSTR pSrc, int nMax=-1); +// inline int sscpy(PWSTR pDst, PCWSTR pSrc, int nMax=-1); +// inline int sscpy(PWSTR pDst, PCSTR pSrc, int nMax=-1); +// +// DESCRIPTION: +// This function is very much (but not exactly) like strcpy. These +// overloads simplify copying one C-style string into another by allowing +// the caller to specify two different types of strings if necessary. +// +// The strings must NOT overlap +// +// "Character" is expressed in terms of the destination string, not +// the source. If no 'nMax' argument is supplied, then the number of +// characters copied will be sslen(pSrc). A NULL terminator will +// also be added so pDst must actually be big enough to hold nMax+1 +// characters. The return value is the number of characters copied, +// not including the NULL terminator. +// +// PARAMETERS: +// pSrc - the string to be copied FROM. May be a char based string, an +// MBCS string (in Win32 builds) or a wide string (wchar_t). +// pSrc - the string to be copied TO. Also may be either MBCS or wide +// nMax - the maximum number of characters to be copied into szDest. Note +// that this is expressed in whatever a "character" means to pDst. +// If pDst is a wchar_t type string than this will be the maximum +// number of wchar_ts that my be copied. The pDst string must be +// large enough to hold least nMaxChars+1 characters. +// If the caller supplies no argument for nMax this is a signal to +// the routine to copy all the characters in pSrc, regardless of +// how long it is. +// +// RETURN VALUE: none +// ----------------------------------------------------------------------------- + +template<typename CT1, typename CT2> +inline int sscpycvt(CT1* pDst, const CT2* pSrc, int nMax) +{ + // Note -- we assume pDst is big enough to hold pSrc. If not, we're in + // big trouble. No bounds checking. Caveat emptor. + + int nSrc = sslen(pSrc); + + const CT1* szCvt = StdCodeCvt(pDst, nMax, pSrc, nSrc); + + // If we're copying the same size characters, then all the "code convert" + // just did was basically memcpy so the #of characters copied is the same + // as the number requested. I should probably specialize this function + // template to achieve this purpose as it is silly to do a runtime check + // of a fact known at compile time. I'll get around to it. + + return sslen(szCvt); +} + +template<typename T> +inline int sscpycvt(T* pDst, const T* pSrc, int nMax) +{ + int nCount = nMax; + for (; nCount > 0 && *pSrc; ++pSrc, ++pDst, --nCount) + std::basic_string<T>::traits_type::assign(*pDst, *pSrc); + + *pDst = 0; + return nMax - nCount; +} + +inline int sscpycvt(PWSTR pDst, PCSTR pSrc, int nMax) +{ + // Note -- we assume pDst is big enough to hold pSrc. If not, we're in + // big trouble. No bounds checking. Caveat emptor. + + const PWSTR szCvt = StdCodeCvt(pDst, nMax, pSrc, nMax); + return sslen(szCvt); +} + +template<typename CT1, typename CT2> +inline int sscpy(CT1* pDst, const CT2* pSrc, int nMax, int nLen) +{ + return sscpycvt(pDst, pSrc, SSMIN(nMax, nLen)); +} +template<typename CT1, typename CT2> +inline int sscpy(CT1* pDst, const CT2* pSrc, int nMax) +{ + return sscpycvt(pDst, pSrc, SSMIN(nMax, sslen(pSrc))); +} +template<typename CT1, typename CT2> +inline int sscpy(CT1* pDst, const CT2* pSrc) +{ + return sscpycvt(pDst, pSrc, sslen(pSrc)); +} +template<typename CT1, typename CT2> +inline int sscpy(CT1* pDst, const std::basic_string<CT2>& sSrc, int nMax) +{ + return sscpycvt(pDst, sSrc.c_str(), SSMIN(nMax, (int)sSrc.length())); +} +template<typename CT1, typename CT2> +inline int sscpy(CT1* pDst, const std::basic_string<CT2>& sSrc) +{ + return sscpycvt(pDst, sSrc.c_str(), (int)sSrc.length()); +} + +#ifdef SS_INC_COMDEF + template<typename CT1> + inline int sscpy(CT1* pDst, const _bstr_t& bs, int nMax) + { + return sscpycvt(pDst, static_cast<PCOLESTR>(bs), + SSMIN(nMax, static_cast<int>(bs.length()))); + } + template<typename CT1> + inline int sscpy(CT1* pDst, const _bstr_t& bs) + { + return sscpy(pDst, bs, static_cast<int>(bs.length())); + } +#endif + + +// ----------------------------------------------------------------------------- +// Functional objects for changing case. They also let you pass locales +// ----------------------------------------------------------------------------- + +#ifdef SS_NO_LOCALE + template<typename CT> + struct SSToUpper : public std::unary_function<CT, CT> + { + inline CT operator()(const CT& t) const + { + return sstoupper(t); + } + }; + template<typename CT> + struct SSToLower : public std::unary_function<CT, CT> + { + inline CT operator()(const CT& t) const + { + return sstolower(t); + } + }; +#else + template<typename CT> + struct SSToUpper : public std::binary_function<CT, std::locale, CT> + { + inline CT operator()(const CT& t, const std::locale& loc) const + { + return sstoupper<CT>(t, loc); + } + }; + template<typename CT> + struct SSToLower : public std::binary_function<CT, std::locale, CT> + { + inline CT operator()(const CT& t, const std::locale& loc) const + { + return sstolower<CT>(t, loc); + } + }; +#endif + +// This struct is used for TrimRight() and TrimLeft() function implementations. +//template<typename CT> +//struct NotSpace : public std::unary_function<CT, bool> +//{ +// const std::locale& loc; +// inline NotSpace(const std::locale& locArg) : loc(locArg) {} +// inline bool operator() (CT t) { return !std::isspace(t, loc); } +//}; +template<typename CT> +struct NotSpace : public std::unary_function<CT, bool> +{ + // DINKUMWARE BUG: + // Note -- using std::isspace in a COM DLL gives us access violations + // because it causes the dynamic addition of a function to be called + // when the library shuts down. Unfortunately the list is maintained + // in DLL memory but the function is in static memory. So the COM DLL + // goes away along with the function that was supposed to be called, + // and then later when the DLL CRT shuts down it unloads the list and + // tries to call the long-gone function. + // This is DinkumWare's implementation problem. If you encounter this + // problem, you may replace the calls here with good old isspace() and + // iswspace() from the CRT unless they specify SS_ANSI + +#ifdef SS_NO_LOCALE + + bool operator() (CT t) const { return !ssisspace(t); } + +#else + const std::locale loc; + NotSpace(const std::locale& locArg=std::locale()) : loc(locArg) {} + bool operator() (CT t) const { return !std::isspace(t, loc); } +#endif +}; + + + + +// Now we can define the template (finally!) +// ============================================================================= +// TEMPLATE: CStdStr +// template<typename CT> class CStdStr : public std::basic_string<CT> +// +// REMARKS: +// This template derives from basic_string<CT> and adds some MFC CString- +// like functionality +// +// Basically, this is my attempt to make Standard C++ library strings as +// easy to use as the MFC CString class. +// +// Note that although this is a template, it makes the assumption that the +// template argument (CT, the character type) is either char or wchar_t. +// ============================================================================= + +//#define CStdStr _SS // avoid compiler warning 4786 + +// template<typename ARG> ARG& FmtArg(ARG& arg) { return arg; } +// PCSTR FmtArg(const std::string& arg) { return arg.c_str(); } +// PCWSTR FmtArg(const std::wstring& arg) { return arg.c_str(); } + +template<typename ARG> +struct FmtArg +{ + explicit FmtArg(const ARG& arg) : a_(arg) {} + const ARG& operator()() const { return a_; } + const ARG& a_; +private: + FmtArg& operator=(const FmtArg&) { return *this; } +}; + +template<typename CT> +class CStdStr : public std::basic_string<CT> +{ + // Typedefs for shorter names. Using these names also appears to help + // us avoid some ambiguities that otherwise arise on some platforms + + #define MYBASE std::basic_string<CT> // my base class + //typedef typename std::basic_string<CT> MYBASE; // my base class + typedef CStdStr<CT> MYTYPE; // myself + typedef typename MYBASE::const_pointer PCMYSTR; // PCSTR or PCWSTR + typedef typename MYBASE::pointer PMYSTR; // PSTR or PWSTR + typedef typename MYBASE::iterator MYITER; // my iterator type + typedef typename MYBASE::const_iterator MYCITER; // you get the idea... + typedef typename MYBASE::reverse_iterator MYRITER; + typedef typename MYBASE::size_type MYSIZE; + typedef typename MYBASE::value_type MYVAL; + typedef typename MYBASE::allocator_type MYALLOC; + +public: + // shorthand conversion from PCTSTR to string resource ID + #define SSRES(pctstr) LOWORD(reinterpret_cast<unsigned long>(pctstr)) + + bool TryLoad(const void* pT) + { + bool bLoaded = false; + +#if defined(SS_WIN32) && !defined(SS_ANSI) + if ( ( pT != NULL ) && SS_IS_INTRESOURCE(pT) ) + { + UINT nId = LOWORD(reinterpret_cast<unsigned long>(pT)); + if ( !LoadString(nId) ) + { + TRACE(_T("Can't load string %u\n"), SSRES(pT)); + } + bLoaded = true; + } +#endif + + return bLoaded; + } + + + // CStdStr inline constructors + CStdStr() + { + } + + CStdStr(const MYTYPE& str) : MYBASE(SSREF(str)) + { + } + + CStdStr(const std::string& str) + { + ssasn(*this, SSREF(str)); + } + + CStdStr(const std::wstring& str) + { + ssasn(*this, SSREF(str)); + } + + CStdStr(PCMYSTR pT, MYSIZE n) : MYBASE(pT, n) + { + } + +#ifdef SS_UNSIGNED + CStdStr(PCUSTR pU) + { + *this = reinterpret_cast<PCSTR>(pU); + } +#endif + + CStdStr(PCSTR pA) + { + #ifdef SS_ANSI + *this = pA; + #else + if ( !TryLoad(pA) ) + *this = pA; + #endif + } + + CStdStr(PCWSTR pW) + { + #ifdef SS_ANSI + *this = pW; + #else + if ( !TryLoad(pW) ) + *this = pW; + #endif + } + + CStdStr(uint16_t* pW) + { + #ifdef SS_ANSI + *this = pW; + #else + if ( !TryLoad(pW) ) + *this = pW; + #endif + } + + CStdStr(uint32_t* pW) + { + #ifdef SS_ANSI + *this = pW; + #else + if ( !TryLoad(pW) ) + *this = pW; + #endif + } + + CStdStr(MYCITER first, MYCITER last) + : MYBASE(first, last) + { + } + + CStdStr(MYSIZE nSize, MYVAL ch, const MYALLOC& al=MYALLOC()) + : MYBASE(nSize, ch, al) + { + } + + #ifdef SS_INC_COMDEF + CStdStr(const _bstr_t& bstr) + { + if ( bstr.length() > 0 ) + this->append(static_cast<PCMYSTR>(bstr), bstr.length()); + } + #endif + + // CStdStr inline assignment operators -- the ssasn function now takes care + // of fixing the MSVC assignment bug (see knowledge base article Q172398). + MYTYPE& operator=(const MYTYPE& str) + { + ssasn(*this, str); + return *this; + } + + MYTYPE& operator=(const std::string& str) + { + ssasn(*this, str); + return *this; + } + + MYTYPE& operator=(const std::wstring& str) + { + ssasn(*this, str); + return *this; + } + + MYTYPE& operator=(PCSTR pA) + { + ssasn(*this, pA); + return *this; + } + + MYTYPE& operator=(PCWSTR pW) + { + ssasn(*this, pW); + return *this; + } + +#ifdef SS_UNSIGNED + MYTYPE& operator=(PCUSTR pU) + { + ssasn(*this, reinterpret_cast<PCSTR>(pU)); + return *this; + } +#endif + + MYTYPE& operator=(uint16_t* pA) + { + ssasn(*this, pA); + return *this; + } + + MYTYPE& operator=(uint32_t* pA) + { + ssasn(*this, pA); + return *this; + } + + MYTYPE& operator=(CT t) + { + Q172398(*this); + this->assign(1, t); + return *this; + } + + #ifdef SS_INC_COMDEF + MYTYPE& operator=(const _bstr_t& bstr) + { + if ( bstr.length() > 0 ) + { + this->assign(static_cast<PCMYSTR>(bstr), bstr.length()); + return *this; + } + else + { + this->erase(); + return *this; + } + } + #endif + + + // Overloads also needed to fix the MSVC assignment bug (KB: Q172398) + // *** Thanks to Pete The Plumber for catching this one *** + // They also are compiled if you have explicitly turned off refcounting + #if ( defined(_MSC_VER) && ( _MSC_VER < 1200 ) ) || defined(SS_NO_REFCOUNT) + + MYTYPE& assign(const MYTYPE& str) + { + Q172398(*this); + sscpy(GetBuffer(str.size()+1), SSREF(str)); + this->ReleaseBuffer(str.size()); + return *this; + } + + MYTYPE& assign(const MYTYPE& str, MYSIZE nStart, MYSIZE nChars) + { + // This overload of basic_string::assign is supposed to assign up to + // <nChars> or the NULL terminator, whichever comes first. Since we + // are about to call a less forgiving overload (in which <nChars> + // must be a valid length), we must adjust the length here to a safe + // value. Thanks to Ullrich Poll�hne for catching this bug + + nChars = SSMIN(nChars, str.length() - nStart); + MYTYPE strTemp(str.c_str()+nStart, nChars); + Q172398(*this); + this->assign(strTemp); + return *this; + } + + MYTYPE& assign(const MYBASE& str) + { + ssasn(*this, str); + return *this; + } + + MYTYPE& assign(const MYBASE& str, MYSIZE nStart, MYSIZE nChars) + { + // This overload of basic_string::assign is supposed to assign up to + // <nChars> or the NULL terminator, whichever comes first. Since we + // are about to call a less forgiving overload (in which <nChars> + // must be a valid length), we must adjust the length here to a safe + // value. Thanks to Ullrich Poll�hne for catching this bug + + nChars = SSMIN(nChars, str.length() - nStart); + + // Watch out for assignment to self + + if ( this == &str ) + { + MYTYPE strTemp(str.c_str() + nStart, nChars); + static_cast<MYBASE*>(this)->assign(strTemp); + } + else + { + Q172398(*this); + static_cast<MYBASE*>(this)->assign(str.c_str()+nStart, nChars); + } + return *this; + } + + MYTYPE& assign(const CT* pC, MYSIZE nChars) + { + // Q172398 only fix -- erase before assigning, but not if we're + // assigning from our own buffer + + #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 ) + if ( !this->empty() && + ( pC < this->data() || pC > this->data() + this->capacity() ) ) + { + this->erase(); + } + #endif + Q172398(*this); + static_cast<MYBASE*>(this)->assign(pC, nChars); + return *this; + } + + MYTYPE& assign(MYSIZE nChars, MYVAL val) + { + Q172398(*this); + static_cast<MYBASE*>(this)->assign(nChars, val); + return *this; + } + + MYTYPE& assign(const CT* pT) + { + return this->assign(pT, MYBASE::traits_type::length(pT)); + } + + MYTYPE& assign(MYCITER iterFirst, MYCITER iterLast) + { + #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 ) + // Q172398 fix. don't call erase() if we're assigning from ourself + if ( iterFirst < this->begin() || + iterFirst > this->begin() + this->size() ) + { + this->erase() + } + #endif + this->replace(this->begin(), this->end(), iterFirst, iterLast); + return *this; + } + #endif + + + // ------------------------------------------------------------------------- + // CStdStr inline concatenation. + // ------------------------------------------------------------------------- + MYTYPE& operator+=(const MYTYPE& str) + { + ssadd(*this, str); + return *this; + } + + MYTYPE& operator+=(const std::string& str) + { + ssadd(*this, str); + return *this; + } + + MYTYPE& operator+=(const std::wstring& str) + { + ssadd(*this, str); + return *this; + } + + MYTYPE& operator+=(PCSTR pA) + { + ssadd(*this, pA); + return *this; + } + + MYTYPE& operator+=(PCWSTR pW) + { + ssadd(*this, pW); + return *this; + } + + MYTYPE& operator+=(uint16_t* pW) + { + ssadd(*this, pW); + return *this; + } + + MYTYPE& operator+=(uint32_t* pW) + { + ssadd(*this, pW); + return *this; + } + + MYTYPE& operator+=(CT t) + { + this->append(1, t); + return *this; + } + #ifdef SS_INC_COMDEF // if we have _bstr_t, define a += for it too. + MYTYPE& operator+=(const _bstr_t& bstr) + { + return this->operator+=(static_cast<PCMYSTR>(bstr)); + } + #endif + + + // ------------------------------------------------------------------------- + // Case changing functions + // ------------------------------------------------------------------------- + + MYTYPE& ToUpper(const std::locale& loc=std::locale()) + { + // Note -- if there are any MBCS character sets in which the lowercase + // form a character takes up a different number of bytes than the + // uppercase form, this would probably not work... + + std::transform(this->begin(), + this->end(), + this->begin(), +#ifdef SS_NO_LOCALE + SSToUpper<CT>()); +#else + std::bind2nd(SSToUpper<CT>(), loc)); +#endif + + // ...but if it were, this would probably work better. Also, this way + // seems to be a bit faster when anything other then the "C" locale is + // used... + +// if ( !empty() ) +// { +// ssupr(this->GetBuf(), this->size(), loc); +// this->RelBuf(); +// } + + return *this; + } + + MYTYPE& ToLower(const std::locale& loc=std::locale()) + { + // Note -- if there are any MBCS character sets in which the lowercase + // form a character takes up a different number of bytes than the + // uppercase form, this would probably not work... + + std::transform(this->begin(), + this->end(), + this->begin(), +#ifdef SS_NO_LOCALE + SSToLower<CT>()); +#else + std::bind2nd(SSToLower<CT>(), loc)); +#endif + + // ...but if it were, this would probably work better. Also, this way + // seems to be a bit faster when anything other then the "C" locale is + // used... + +// if ( !empty() ) +// { +// sslwr(this->GetBuf(), this->size(), loc); +// this->RelBuf(); +// } + return *this; + } + + + MYTYPE& Normalize() + { + return Trim().ToLower(); + } + + + // ------------------------------------------------------------------------- + // CStdStr -- Direct access to character buffer. In the MS' implementation, + // the at() function that we use here also calls _Freeze() providing us some + // protection from multithreading problems associated with ref-counting. + // In VC 7 and later, of course, the ref-counting stuff is gone. + // ------------------------------------------------------------------------- + + CT* GetBuf(int nMinLen=-1) + { + if ( static_cast<int>(this->size()) < nMinLen ) + this->resize(static_cast<MYSIZE>(nMinLen)); + + return this->empty() ? const_cast<CT*>(this->data()) : &(this->at(0)); + } + + CT* SetBuf(int nLen) + { + nLen = ( nLen > 0 ? nLen : 0 ); + if ( this->capacity() < 1 && nLen == 0 ) + this->resize(1); + + this->resize(static_cast<MYSIZE>(nLen)); + return const_cast<CT*>(this->data()); + } + void RelBuf(int nNewLen=-1) + { + this->resize(static_cast<MYSIZE>(nNewLen > -1 ? nNewLen : + sslen(this->c_str()))); + } + + void BufferRel() { RelBuf(); } // backwards compatability + CT* Buffer() { return GetBuf(); } // backwards compatability + CT* BufferSet(int nLen) { return SetBuf(nLen);}// backwards compatability + + bool Equals(const CT* pT, bool bUseCase=false) const + { + return 0 == (bUseCase ? this->compare(pT) : ssicmp(this->c_str(), pT)); + } + + // ------------------------------------------------------------------------- + // FUNCTION: CStdStr::Load + // REMARKS: + // Loads string from resource specified by nID + // + // PARAMETERS: + // nID - resource Identifier. Purely a Win32 thing in this case + // + // RETURN VALUE: + // true if successful, false otherwise + // ------------------------------------------------------------------------- + +#ifndef SS_ANSI + + bool Load(UINT nId, HMODULE hModule=NULL) + { + bool bLoaded = false; // set to true of we succeed. + + #ifdef _MFC_VER // When in Rome (or MFC land)... + + // If they gave a resource handle, use it. Note - this is archaic + // and not really what I would recommend. But then again, in MFC + // land, you ought to be using CString for resources anyway since + // it walks the resource chain for you. + + HMODULE hModuleOld = NULL; + + if ( NULL != hModule ) + { + hModuleOld = AfxGetResourceHandle(); + AfxSetResourceHandle(hModule); + } + + // ...load the string + + CString strRes; + bLoaded = FALSE != strRes.LoadString(nId); + + // ...and if we set the resource handle, restore it. + + if ( NULL != hModuleOld ) + AfxSetResourceHandle(hModule); + + if ( bLoaded ) + *this = strRes; + + #else // otherwise make our own hackneyed version of CString's Load + + // Get the resource name and module handle + + if ( NULL == hModule ) + hModule = GetResourceHandle(); + + PCTSTR szName = MAKEINTRESOURCE((nId>>4)+1); // lifted + DWORD dwSize = 0; + + // No sense continuing if we can't find the resource + + HRSRC hrsrc = ::FindResource(hModule, szName, RT_STRING); + + if ( NULL == hrsrc ) + { + TRACE(_T("Cannot find resource %d: 0x%X"), nId, ::GetLastError()); + } + else if ( 0 == (dwSize = ::SizeofResource(hModule, hrsrc) / sizeof(CT))) + { + TRACE(_T("Cant get size of resource %d 0x%X\n"),nId,GetLastError()); + } + else + { + bLoaded = 0 != ssload(hModule, nId, GetBuf(dwSize), dwSize); + ReleaseBuffer(); + } + + #endif // #ifdef _MFC_VER + + if ( !bLoaded ) + TRACE(_T("String not loaded 0x%X\n"), ::GetLastError()); + + return bLoaded; + } + +#endif // #ifdef SS_ANSI + + // ------------------------------------------------------------------------- + // FUNCTION: CStdStr::Format + // void _cdecl Formst(CStdStringA& PCSTR szFormat, ...) + // void _cdecl Format(PCSTR szFormat); + // + // DESCRIPTION: + // This function does sprintf/wsprintf style formatting on CStdStringA + // objects. It looks a lot like MFC's CString::Format. Some people + // might even call this identical. Fortunately, these people are now + // dead... heh heh. + // + // PARAMETERS: + // nId - ID of string resource holding the format string + // szFormat - a PCSTR holding the format specifiers + // argList - a va_list holding the arguments for the format specifiers. + // + // RETURN VALUE: None. + // ------------------------------------------------------------------------- + // formatting (using wsprintf style formatting) + + // If they want a Format() function that safely handles string objects + // without casting + +#ifdef SS_SAFE_FORMAT + + // Question: Joe, you wacky coder you, why do you have so many overloads + // of the Format() function + // Answer: One reason only - CString compatability. In short, by making + // the Format() function a template this way, I can do strong typing + // and allow people to pass CStdString arguments as fillers for + // "%s" format specifiers without crashing their program! The downside + // is that I need to overload on the number of arguments. If you are + // passing more arguments than I have listed below in any of my + // overloads, just add another one. + // + // Yes, yes, this is really ugly. In essence what I am doing here is + // protecting people from a bad (and incorrect) programming practice + // that they should not be doing anyway. I am protecting them from + // themselves. Why am I doing this? Well, if you had any idea the + // number of times I've been emailed by people about this + // "incompatability" in my code, you wouldn't ask. + + void Fmt(const CT* szFmt, ...) + { + va_list argList; + va_start(argList, szFmt); + FormatV(szFmt, argList); + va_end(argList); + } + +#ifndef SS_ANSI + + void Format(UINT nId) + { + MYTYPE strFmt; + if ( strFmt.Load(nId) ) + this->swap(strFmt); + } + template<class A1> + void Format(UINT nId, const A1& v) + { + MYTYPE strFmt; + if ( strFmt.Load(nId) ) + Fmt(strFmt, FmtArg<A1>(v)()); + } + template<class A1, class A2> + void Format(UINT nId, const A1& v1, const A2& v2) + { + MYTYPE strFmt; + if ( strFmt.Load(nId) ) + Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)()); + } + template<class A1, class A2, class A3> + void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3) + { + MYTYPE strFmt; + if ( strFmt.Load(nId) ) + { + Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)()); + } + } + template<class A1, class A2, class A3, class A4> + void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3, + const A4& v4) + { + MYTYPE strFmt; + if ( strFmt.Load(nId) ) + { + Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)()); + } + } + template<class A1, class A2, class A3, class A4, class A5> + void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5) + { + MYTYPE strFmt; + if ( strFmt.Load(nId) ) + { + Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)()); + } + } + template<class A1, class A2, class A3, class A4, class A5, class A6> + void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6) + { + MYTYPE strFmt; + if ( strFmt.Load(nId) ) + { + Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(),FmtArg<A5>(v5)(), + FmtArg<A6>(v6)()); + } + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7> + void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7) + { + MYTYPE strFmt; + if ( strFmt.Load(nId) ) + { + Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(),FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)()); + } + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7, class A8> + void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7, + const A8& v8) + { + MYTYPE strFmt; + if ( strFmt.Load(nId) ) + { + Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)()); + } + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7, class A8, class A9> + void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7, + const A8& v8, const A9& v9) + { + MYTYPE strFmt; + if ( strFmt.Load(nId) ) + { + Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(), + FmtArg<A9>(v9)()); + } + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7, class A8, class A9, class A10> + void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7, + const A8& v8, const A9& v9, const A10& v10) + { + MYTYPE strFmt; + if ( strFmt.Load(nId) ) + { + Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(), + FmtArg<A9>(v9)(), FmtArg<A10>(v10)()); + } + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7, class A8, class A9, class A10, class A11> + void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7, + const A8& v8, const A9& v9, const A10& v10, const A11& v11) + { + MYTYPE strFmt; + if ( strFmt.Load(nId) ) + { + Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(), + FmtArg<A9>(v9)(),FmtArg<A10>(v10)(),FmtArg<A11>(v11)()); + } + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7, class A8, class A9, class A10, class A11, class A12> + void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7, + const A8& v8, const A9& v9, const A10& v10, const A11& v11, + const A12& v12) + { + MYTYPE strFmt; + if ( strFmt.Load(nId) ) + { + Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(), + FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(), + FmtArg<A12>(v12)()); + } + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7, class A8, class A9, class A10, class A11, class A12, + class A13> + void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7, + const A8& v8, const A9& v9, const A10& v10, const A11& v11, + const A12& v12, const A13& v13) + { + MYTYPE strFmt; + if ( strFmt.Load(nId) ) + { + Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(), + FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(), + FmtArg<A12>(v12)(), FmtArg<A13>(v13)()); + } + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7, class A8, class A9, class A10, class A11, class A12, + class A13, class A14> + void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7, + const A8& v8, const A9& v9, const A10& v10, const A11& v11, + const A12& v12, const A13& v13, const A14& v14) + { + MYTYPE strFmt; + if ( strFmt.Load(nId) ) + { + Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(), + FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(), + FmtArg<A12>(v12)(), FmtArg<A13>(v13)(),FmtArg<A14>(v14)()); + } + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7, class A8, class A9, class A10, class A11, class A12, + class A13, class A14, class A15> + void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7, + const A8& v8, const A9& v9, const A10& v10, const A11& v11, + const A12& v12, const A13& v13, const A14& v14, const A15& v15) + { + MYTYPE strFmt; + if ( strFmt.Load(nId) ) + { + Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(), + FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(), + FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(), + FmtArg<A15>(v15)()); + } + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7, class A8, class A9, class A10, class A11, class A12, + class A13, class A14, class A15, class A16> + void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7, + const A8& v8, const A9& v9, const A10& v10, const A11& v11, + const A12& v12, const A13& v13, const A14& v14, const A15& v15, + const A16& v16) + { + MYTYPE strFmt; + if ( strFmt.Load(nId) ) + { + Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(), + FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(), + FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(), + FmtArg<A15>(v15)(), FmtArg<A16>(v16)()); + } + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7, class A8, class A9, class A10, class A11, class A12, + class A13, class A14, class A15, class A16, class A17> + void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7, + const A8& v8, const A9& v9, const A10& v10, const A11& v11, + const A12& v12, const A13& v13, const A14& v14, const A15& v15, + const A16& v16, const A17& v17) + { + MYTYPE strFmt; + if ( strFmt.Load(nId) ) + { + Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(), + FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(), + FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(), + FmtArg<A15>(v15)(),FmtArg<A16>(v16)(),FmtArg<A17>(v17)()); + } + } + +#endif // #ifndef SS_ANSI + + // ...now the other overload of Format: the one that takes a string literal + + void Format(const CT* szFmt) + { + *this = szFmt; + } + template<class A1> + void Format(const CT* szFmt, const A1& v) + { + Fmt(szFmt, FmtArg<A1>(v)()); + } + template<class A1, class A2> + void Format(const CT* szFmt, const A1& v1, const A2& v2) + { + Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)()); + } + template<class A1, class A2, class A3> + void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3) + { + Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)()); + } + template<class A1, class A2, class A3, class A4> + void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3, + const A4& v4) + { + Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)()); + } + template<class A1, class A2, class A3, class A4, class A5> + void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5) + { + Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)()); + } + template<class A1, class A2, class A3, class A4, class A5, class A6> + void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6) + { + Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)()); + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7> + void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7) + { + Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)()); + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7, class A8> + void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7, + const A8& v8) + { + Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)()); + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7, class A8, class A9> + void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7, + const A8& v8, const A9& v9) + { + Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(), + FmtArg<A9>(v9)()); + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7, class A8, class A9, class A10> + void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7, + const A8& v8, const A9& v9, const A10& v10) + { + Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(), + FmtArg<A9>(v9)(), FmtArg<A10>(v10)()); + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7, class A8, class A9, class A10, class A11> + void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7, + const A8& v8, const A9& v9, const A10& v10, const A11& v11) + { + Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(), + FmtArg<A9>(v9)(),FmtArg<A10>(v10)(),FmtArg<A11>(v11)()); + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7, class A8, class A9, class A10, class A11, class A12> + void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7, + const A8& v8, const A9& v9, const A10& v10, const A11& v11, + const A12& v12) + { + Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(), + FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(), + FmtArg<A12>(v12)()); + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7, class A8, class A9, class A10, class A11, class A12, + class A13> + void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7, + const A8& v8, const A9& v9, const A10& v10, const A11& v11, + const A12& v12, const A13& v13) + { + Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(), + FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(), + FmtArg<A12>(v12)(), FmtArg<A13>(v13)()); + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7, class A8, class A9, class A10, class A11, class A12, + class A13, class A14> + void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7, + const A8& v8, const A9& v9, const A10& v10, const A11& v11, + const A12& v12, const A13& v13, const A14& v14) + { + Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(), + FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(), + FmtArg<A12>(v12)(), FmtArg<A13>(v13)(),FmtArg<A14>(v14)()); + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7, class A8, class A9, class A10, class A11, class A12, + class A13, class A14, class A15> + void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7, + const A8& v8, const A9& v9, const A10& v10, const A11& v11, + const A12& v12, const A13& v13, const A14& v14, const A15& v15) + { + Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(), + FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(), + FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(), + FmtArg<A15>(v15)()); + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7, class A8, class A9, class A10, class A11, class A12, + class A13, class A14, class A15, class A16> + void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7, + const A8& v8, const A9& v9, const A10& v10, const A11& v11, + const A12& v12, const A13& v13, const A14& v14, const A15& v15, + const A16& v16) + { + Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(), + FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(), + FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(), + FmtArg<A15>(v15)(), FmtArg<A16>(v16)()); + } + template<class A1, class A2, class A3, class A4, class A5, class A6, + class A7, class A8, class A9, class A10, class A11, class A12, + class A13, class A14, class A15, class A16, class A17> + void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3, + const A4& v4, const A5& v5, const A6& v6, const A7& v7, + const A8& v8, const A9& v9, const A10& v10, const A11& v11, + const A12& v12, const A13& v13, const A14& v14, const A15& v15, + const A16& v16, const A17& v17) + { + Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(), + FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(), + FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(), + FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(), + FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(), + FmtArg<A15>(v15)(),FmtArg<A16>(v16)(),FmtArg<A17>(v17)()); + } + +#else // #ifdef SS_SAFE_FORMAT + + +#ifndef SS_ANSI + + void Format(UINT nId, ...) + { + va_list argList; + va_start(argList, nId); + + MYTYPE strFmt; + if ( strFmt.Load(nId) ) + FormatV(strFmt, argList); + + va_end(argList); + } + +#endif // #ifdef SS_ANSI + + void Format(const CT* szFmt, ...) + { + va_list argList; + va_start(argList, szFmt); + FormatV(szFmt, argList); + va_end(argList); + } + +#endif // #ifdef SS_SAFE_FORMAT + + void AppendFormat(const CT* szFmt, ...) + { + va_list argList; + va_start(argList, szFmt); + AppendFormatV(szFmt, argList); + va_end(argList); + } + + #define MAX_FMT_TRIES 5 // #of times we try + #define FMT_BLOCK_SIZE 2048 // # of bytes to increment per try + #define BUFSIZE_1ST 256 + #define BUFSIZE_2ND 512 + #define STD_BUF_SIZE 1024 + + // an efficient way to add formatted characters to the string. You may only + // add up to STD_BUF_SIZE characters at a time, though + void AppendFormatV(const CT* szFmt, va_list argList) + { + CT szBuf[STD_BUF_SIZE]; + int nLen = ssnprintf(szBuf, STD_BUF_SIZE-1, szFmt, argList); + + if ( 0 < nLen ) + this->append(szBuf, nLen); + } + + // ------------------------------------------------------------------------- + // FUNCTION: FormatV + // void FormatV(PCSTR szFormat, va_list, argList); + // + // DESCRIPTION: + // This function formats the string with sprintf style format-specs. + // It makes a general guess at required buffer size and then tries + // successively larger buffers until it finds one big enough or a + // threshold (MAX_FMT_TRIES) is exceeded. + // + // PARAMETERS: + // szFormat - a PCSTR holding the format of the output + // argList - a Microsoft specific va_list for variable argument lists + // + // RETURN VALUE: + // ------------------------------------------------------------------------- + + // NOTE: Changed by JM to actually function under non-win32, + // and to remove the upper limit on size. + void FormatV(const CT* szFormat, va_list argList) + { + // try and grab a sufficient buffersize + int nChars = FMT_BLOCK_SIZE; + va_list argCopy; + + CT *p = reinterpret_cast<CT*>(malloc(sizeof(CT)*nChars)); + if (!p) return; + + while (1) + { + va_copy(argCopy, argList); + + int nActual = ssvsprintf(p, nChars, szFormat, argCopy); + /* If that worked, return the string. */ + if (nActual > -1 && nActual < nChars) + { /* make sure it's NULL terminated */ + p[nActual] = '\0'; + this->assign(p, nActual); + free(p); + va_end(argCopy); + return; + } + /* Else try again with more space. */ + if (nActual > -1) /* glibc 2.1 */ + nChars = nActual + 1; /* precisely what is needed */ + else /* glibc 2.0 */ + nChars *= 2; /* twice the old size */ + + CT *np = reinterpret_cast<CT*>(realloc(p, sizeof(CT)*nChars)); + if (np == NULL) + { + free(p); + va_end(argCopy); + return; // failed :( + } + p = np; + va_end(argCopy); + } + } + + // ------------------------------------------------------------------------- + // CString Facade Functions: + // + // The following methods are intended to allow you to use this class as a + // near drop-in replacement for CString. + // ------------------------------------------------------------------------- + #ifdef SS_WIN32 + BSTR AllocSysString() const + { + ostring os; + ssasn(os, *this); + return ::SysAllocString(os.c_str()); + } + #endif + +#ifndef SS_NO_LOCALE + int Collate(PCMYSTR szThat) const + { + return sscoll(this->c_str(), this->length(), szThat, sslen(szThat)); + } + + int CollateNoCase(PCMYSTR szThat) const + { + return ssicoll(this->c_str(), this->length(), szThat, sslen(szThat)); + } +#endif + int Compare(PCMYSTR szThat) const + { + return this->compare(szThat); + } + + int CompareNoCase(PCMYSTR szThat) const + { + return ssicmp(this->c_str(), szThat); + } + + int Delete(int nIdx, int nCount=1) + { + if ( nIdx < 0 ) + nIdx = 0; + + if ( nIdx < this->GetLength() ) + this->erase(static_cast<MYSIZE>(nIdx), static_cast<MYSIZE>(nCount)); + + return GetLength(); + } + + void Empty() + { + this->erase(); + } + + int Find(CT ch) const + { + MYSIZE nIdx = this->find_first_of(ch); + return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx); + } + + int Find(PCMYSTR szSub) const + { + MYSIZE nIdx = this->find(szSub); + return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx); + } + + int Find(CT ch, int nStart) const + { + // CString::Find docs say add 1 to nStart when it's not zero + // CString::Find code doesn't do that however. We'll stick + // with what the code does + + MYSIZE nIdx = this->find_first_of(ch, static_cast<MYSIZE>(nStart)); + return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx); + } + + int Find(PCMYSTR szSub, int nStart) const + { + // CString::Find docs say add 1 to nStart when it's not zero + // CString::Find code doesn't do that however. We'll stick + // with what the code does + + MYSIZE nIdx = this->find(szSub, static_cast<MYSIZE>(nStart)); + return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx); + } + + int FindOneOf(PCMYSTR szCharSet) const + { + MYSIZE nIdx = this->find_first_of(szCharSet); + return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx); + } + +#ifndef SS_ANSI + void FormatMessage(PCMYSTR szFormat, ...) throw(std::exception) + { + va_list argList; + va_start(argList, szFormat); + PMYSTR szTemp; + if ( ssfmtmsg(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER, + szFormat, 0, 0, + reinterpret_cast<PMYSTR>(&szTemp), 0, &argList) == 0 || + szTemp == 0 ) + { + throw std::runtime_error("out of memory"); + } + *this = szTemp; + LocalFree(szTemp); + va_end(argList); + } + + void FormatMessage(UINT nFormatId, ...) throw(std::exception) + { + MYTYPE sFormat; + VERIFY(sFormat.LoadString(nFormatId)); + va_list argList; + va_start(argList, nFormatId); + PMYSTR szTemp; + if ( ssfmtmsg(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER, + sFormat, 0, 0, + reinterpret_cast<PMYSTR>(&szTemp), 0, &argList) == 0 || + szTemp == 0) + { + throw std::runtime_error("out of memory"); + } + *this = szTemp; + LocalFree(szTemp); + va_end(argList); + } +#endif + + // GetAllocLength -- an MSVC7 function but it costs us nothing to add it. + + int GetAllocLength() + { + return static_cast<int>(this->capacity()); + } + + // ------------------------------------------------------------------------- + // GetXXXX -- Direct access to character buffer + // ------------------------------------------------------------------------- + CT GetAt(int nIdx) const + { + return this->at(static_cast<MYSIZE>(nIdx)); + } + + CT* GetBuffer(int nMinLen=-1) + { + return GetBuf(nMinLen); + } + + CT* GetBufferSetLength(int nLen) + { + return BufferSet(nLen); + } + + // GetLength() -- MFC docs say this is the # of BYTES but + // in truth it is the number of CHARACTERs (chars or wchar_ts) + int GetLength() const + { + return static_cast<int>(this->length()); + } + + int Insert(int nIdx, CT ch) + { + if ( static_cast<MYSIZE>(nIdx) > this->size()-1 ) + this->append(1, ch); + else + this->insert(static_cast<MYSIZE>(nIdx), 1, ch); + + return GetLength(); + } + int Insert(int nIdx, PCMYSTR sz) + { + if ( static_cast<MYSIZE>(nIdx) >= this->size() ) + this->append(sz, static_cast<MYSIZE>(sslen(sz))); + else + this->insert(static_cast<MYSIZE>(nIdx), sz); + + return GetLength(); + } + + bool IsEmpty() const + { + return this->empty(); + } + + MYTYPE Left(int nCount) const + { + // Range check the count. + + nCount = SSMAX(0, SSMIN(nCount, static_cast<int>(this->size()))); + return this->substr(0, static_cast<MYSIZE>(nCount)); + } + +#ifndef SS_ANSI + bool LoadString(UINT nId) + { + return this->Load(nId); + } +#endif + + void MakeLower() + { + ToLower(); + } + + void MakeReverse() + { + std::reverse(this->begin(), this->end()); + } + + void MakeUpper() + { + ToUpper(); + } + + MYTYPE Mid(int nFirst) const + { + return Mid(nFirst, this->GetLength()-nFirst); + } + + MYTYPE Mid(int nFirst, int nCount) const + { + // CString does range checking here. Since we're trying to emulate it, + // we must check too. + + if ( nFirst < 0 ) + nFirst = 0; + if ( nCount < 0 ) + nCount = 0; + + int nSize = static_cast<int>(this->size()); + + if ( nFirst + nCount > nSize ) + nCount = nSize - nFirst; + + if ( nFirst > nSize ) + return MYTYPE(); + + ASSERT(nFirst >= 0); + ASSERT(nFirst + nCount <= nSize); + + return this->substr(static_cast<MYSIZE>(nFirst), + static_cast<MYSIZE>(nCount)); + } + + void ReleaseBuffer(int nNewLen=-1) + { + RelBuf(nNewLen); + } + + int Remove(CT ch) + { + MYSIZE nIdx = 0; + int nRemoved = 0; + while ( (nIdx=this->find_first_of(ch)) != MYBASE::npos ) + { + this->erase(nIdx, 1); + nRemoved++; + } + return nRemoved; + } + + int Replace(CT chOld, CT chNew) + { + int nReplaced = 0; + + for ( MYITER iter=this->begin(); iter != this->end(); iter++ ) + { + if ( *iter == chOld ) + { + *iter = chNew; + nReplaced++; + } + } + + return nReplaced; + } + + int Replace(PCMYSTR szOld, PCMYSTR szNew) + { + int nReplaced = 0; + MYSIZE nIdx = 0; + MYSIZE nOldLen = sslen(szOld); + + if ( 0 != nOldLen ) + { + // If the replacement string is longer than the one it replaces, this + // string is going to have to grow in size, Figure out how much + // and grow it all the way now, rather than incrementally + + MYSIZE nNewLen = sslen(szNew); + if ( nNewLen > nOldLen ) + { + int nFound = 0; + while ( nIdx < this->length() && + (nIdx=this->find(szOld, nIdx)) != MYBASE::npos ) + { + nFound++; + nIdx += nOldLen; + } + this->reserve(this->size() + nFound * (nNewLen - nOldLen)); + } + + + static const CT ch = CT(0); + PCMYSTR szRealNew = szNew == 0 ? &ch : szNew; + nIdx = 0; + + while ( nIdx < this->length() && + (nIdx=this->find(szOld, nIdx)) != MYBASE::npos ) + { + this->replace(this->begin()+nIdx, this->begin()+nIdx+nOldLen, + szRealNew); + + nReplaced++; + nIdx += nNewLen; + } + } + + return nReplaced; + } + + int ReverseFind(CT ch) const + { + MYSIZE nIdx = this->find_last_of(ch); + return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx); + } + + // ReverseFind overload that's not in CString but might be useful + int ReverseFind(PCMYSTR szFind, MYSIZE pos=MYBASE::npos) const + { + //yuvalt - this does not compile with g++ since MYTTYPE() is different type + //MYSIZE nIdx = this->rfind(0 == szFind ? MYTYPE() : szFind, pos); + MYSIZE nIdx = this->rfind(0 == szFind ? "" : szFind, pos); + return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx); + } + + MYTYPE Right(int nCount) const + { + // Range check the count. + + nCount = SSMAX(0, SSMIN(nCount, static_cast<int>(this->size()))); + return this->substr(this->size()-static_cast<MYSIZE>(nCount)); + } + + void SetAt(int nIndex, CT ch) + { + ASSERT(this->size() > static_cast<MYSIZE>(nIndex)); + this->at(static_cast<MYSIZE>(nIndex)) = ch; + } + +#ifndef SS_ANSI + BSTR SetSysString(BSTR* pbstr) const + { + ostring os; + ssasn(os, *this); + if ( !::SysReAllocStringLen(pbstr, os.c_str(), os.length()) ) + throw std::runtime_error("out of memory"); + + ASSERT(*pbstr != 0); + return *pbstr; + } +#endif + + MYTYPE SpanExcluding(PCMYSTR szCharSet) const + { + MYSIZE pos = this->find_first_of(szCharSet); + return pos == MYBASE::npos ? *this : Left(pos); + } + + MYTYPE SpanIncluding(PCMYSTR szCharSet) const + { + MYSIZE pos = this->find_first_not_of(szCharSet); + return pos == MYBASE::npos ? *this : Left(pos); + } + +#if defined SS_WIN32 && !defined(UNICODE) && !defined(SS_ANSI) + + // CString's OemToAnsi and AnsiToOem functions are available only in + // Unicode builds. However since we're a template we also need a + // runtime check of CT and a reinterpret_cast to account for the fact + // that CStdStringW gets instantiated even in non-Unicode builds. + + void AnsiToOem() + { + if ( sizeof(CT) == sizeof(char) && !empty() ) + { + ::CharToOem(reinterpret_cast<PCSTR>(this->c_str()), + reinterpret_cast<PSTR>(GetBuf())); + } + else + { + ASSERT(false); + } + } + + void OemToAnsi() + { + if ( sizeof(CT) == sizeof(char) && !empty() ) + { + ::OemToChar(reinterpret_cast<PCSTR>(this->c_str()), + reinterpret_cast<PSTR>(GetBuf())); + } + else + { + ASSERT(false); + } + } + +#endif + + + // ------------------------------------------------------------------------- + // Trim and its variants + // ------------------------------------------------------------------------- + MYTYPE& Trim() + { + return TrimLeft().TrimRight(); + } + + MYTYPE& TrimLeft() + { + this->erase(this->begin(), + std::find_if(this->begin(), this->end(), NotSpace<CT>())); + + return *this; + } + + MYTYPE& TrimLeft(CT tTrim) + { + this->erase(0, this->find_first_not_of(tTrim)); + return *this; + } + + MYTYPE& TrimLeft(PCMYSTR szTrimChars) + { + this->erase(0, this->find_first_not_of(szTrimChars)); + return *this; + } + + MYTYPE& TrimRight() + { + // NOTE: When comparing reverse_iterators here (MYRITER), I avoid using + // operator!=. This is because namespace rel_ops also has a template + // operator!= which conflicts with the global operator!= already defined + // for reverse_iterator in the header <utility>. + // Thanks to John James for alerting me to this. + + MYRITER it = std::find_if(this->rbegin(), this->rend(), NotSpace<CT>()); + if ( !(this->rend() == it) ) + this->erase(this->rend() - it); + + this->erase(!(it == this->rend()) ? this->find_last_of(*it) + 1 : 0); + return *this; + } + + MYTYPE& TrimRight(CT tTrim) + { + MYSIZE nIdx = this->find_last_not_of(tTrim); + this->erase(MYBASE::npos == nIdx ? 0 : ++nIdx); + return *this; + } + + MYTYPE& TrimRight(PCMYSTR szTrimChars) + { + MYSIZE nIdx = this->find_last_not_of(szTrimChars); + this->erase(MYBASE::npos == nIdx ? 0 : ++nIdx); + return *this; + } + + void FreeExtra() + { + MYTYPE mt; + this->swap(mt); + if ( !mt.empty() ) + this->assign(mt.c_str(), mt.size()); + } + + // I have intentionally not implemented the following CString + // functions. You cannot make them work without taking advantage + // of implementation specific behavior. However if you absolutely + // MUST have them, uncomment out these lines for "sort-of-like" + // their behavior. You're on your own. + +// CT* LockBuffer() { return GetBuf(); }// won't really lock +// void UnlockBuffer(); { } // why have UnlockBuffer w/o LockBuffer? + + // Array-indexing operators. Required because we defined an implicit cast + // to operator const CT* (Thanks to Julian Selman for pointing this out) + + CT& operator[](int nIdx) + { + return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx)); + } + + const CT& operator[](int nIdx) const + { + return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx)); + } + + CT& operator[](unsigned int nIdx) + { + return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx)); + } + + const CT& operator[](unsigned int nIdx) const + { + return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx)); + } + + CT& operator[](unsigned long nIdx) + { + return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx)); + } + + const CT& operator[](unsigned long nIdx) const + { + return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx)); + } + +#ifndef SS_NO_IMPLICIT_CAST + operator const CT*() const + { + return this->c_str(); + } +#endif + + // IStream related functions. Useful in IPersistStream implementations + +#ifdef SS_INC_COMDEF + + // struct SSSHDR - useful for non Std C++ persistence schemes. + typedef struct SSSHDR + { + BYTE byCtrl; + ULONG nChars; + } SSSHDR; // as in "Standard String Stream Header" + + #define SSSO_UNICODE 0x01 // the string is a wide string + #define SSSO_COMPRESS 0x02 // the string is compressed + + // ------------------------------------------------------------------------- + // FUNCTION: StreamSize + // REMARKS: + // Returns how many bytes it will take to StreamSave() this CStdString + // object to an IStream. + // ------------------------------------------------------------------------- + ULONG StreamSize() const + { + // Control header plus string + ASSERT(this->size()*sizeof(CT) < 0xffffffffUL - sizeof(SSSHDR)); + return (this->size() * sizeof(CT)) + sizeof(SSSHDR); + } + + // ------------------------------------------------------------------------- + // FUNCTION: StreamSave + // REMARKS: + // Saves this CStdString object to a COM IStream. + // ------------------------------------------------------------------------- + HRESULT StreamSave(IStream* pStream) const + { + ASSERT(this->size()*sizeof(CT) < 0xffffffffUL - sizeof(SSSHDR)); + HRESULT hr = E_FAIL; + ASSERT(pStream != 0); + SSSHDR hdr; + hdr.byCtrl = sizeof(CT) == 2 ? SSSO_UNICODE : 0; + hdr.nChars = this->size(); + + + if ( FAILED(hr=pStream->Write(&hdr, sizeof(SSSHDR), 0)) ) + { + TRACE(_T("StreamSave: Cannot write control header, ERR=0x%X\n"),hr); + } + else if ( empty() ) + { + ; // nothing to write + } + else if ( FAILED(hr=pStream->Write(this->c_str(), + this->size()*sizeof(CT), 0)) ) + { + TRACE(_T("StreamSave: Cannot write string to stream 0x%X\n"), hr); + } + + return hr; + } + + + // ------------------------------------------------------------------------- + // FUNCTION: StreamLoad + // REMARKS: + // This method loads the object from an IStream. + // ------------------------------------------------------------------------- + HRESULT StreamLoad(IStream* pStream) + { + ASSERT(pStream != 0); + SSSHDR hdr; + HRESULT hr = E_FAIL; + + if ( FAILED(hr=pStream->Read(&hdr, sizeof(SSSHDR), 0)) ) + { + TRACE(_T("StreamLoad: Cant read control header, ERR=0x%X\n"), hr); + } + else if ( hdr.nChars > 0 ) + { + ULONG nRead = 0; + PMYSTR pMyBuf = BufferSet(hdr.nChars); + + // If our character size matches the character size of the string + // we're trying to read, then we can read it directly into our + // buffer. Otherwise, we have to read into an intermediate buffer + // and convert. + + if ( (hdr.byCtrl & SSSO_UNICODE) != 0 ) + { + ULONG nBytes = hdr.nChars * sizeof(wchar_t); + if ( sizeof(CT) == sizeof(wchar_t) ) + { + if ( FAILED(hr=pStream->Read(pMyBuf, nBytes, &nRead)) ) + TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr); + } + else + { + PWSTR pBufW = reinterpret_cast<PWSTR>(_alloca((nBytes)+1)); + if ( FAILED(hr=pStream->Read(pBufW, nBytes, &nRead)) ) + TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr); + else + sscpy(pMyBuf, pBufW, hdr.nChars); + } + } + else + { + ULONG nBytes = hdr.nChars * sizeof(char); + if ( sizeof(CT) == sizeof(char) ) + { + if ( FAILED(hr=pStream->Read(pMyBuf, nBytes, &nRead)) ) + TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr); + } + else + { + PSTR pBufA = reinterpret_cast<PSTR>(_alloca(nBytes)); + if ( FAILED(hr=pStream->Read(pBufA, hdr.nChars, &nRead)) ) + TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr); + else + sscpy(pMyBuf, pBufA, hdr.nChars); + } + } + } + else + { + this->erase(); + } + return hr; + } +#endif // #ifdef SS_INC_COMDEF + +#ifndef SS_ANSI + + // SetResourceHandle/GetResourceHandle. In MFC builds, these map directly + // to AfxSetResourceHandle and AfxGetResourceHandle. In non-MFC builds they + // point to a single static HINST so that those who call the member + // functions that take resource IDs can provide an alternate HINST of a DLL + // to search. This is not exactly the list of HMODULES that MFC provides + // but it's better than nothing. + + #ifdef _MFC_VER + static void SetResourceHandle(HMODULE hNew) + { + AfxSetResourceHandle(hNew); + } + static HMODULE GetResourceHandle() + { + return AfxGetResourceHandle(); + } + #else + static void SetResourceHandle(HMODULE hNew) + { + SSResourceHandle() = hNew; + } + static HMODULE GetResourceHandle() + { + return SSResourceHandle(); + } + #endif + +#endif +}; + +// ----------------------------------------------------------------------------- +// MSVC USERS: HOW TO EXPORT CSTDSTRING FROM A DLL +// +// If you are using MS Visual C++ and you want to export CStdStringA and +// CStdStringW from a DLL, then all you need to +// +// 1. make sure that all components link to the same DLL version +// of the CRT (not the static one). +// 2. Uncomment the 3 lines of code below +// 3. #define 2 macros per the instructions in MS KnowledgeBase +// article Q168958. The macros are: +// +// MACRO DEFINTION WHEN EXPORTING DEFINITION WHEN IMPORTING +// ----- ------------------------ ------------------------- +// SSDLLEXP (nothing, just #define it) extern +// SSDLLSPEC __declspec(dllexport) __declspec(dllimport) +// +// Note that these macros must be available to ALL clients who want to +// link to the DLL and use the class. If they +// +// A word of advice: Don't bother. +// +// Really, it is not necessary to export CStdString functions from a DLL. I +// never do. In my projects, I do generally link to the DLL version of the +// Standard C++ Library, but I do NOT attempt to export CStdString functions. +// I simply include the header where it is needed and allow for the code +// redundancy. +// +// That redundancy is a lot less than you think. This class does most of its +// work via the Standard C++ Library, particularly the base_class basic_string<> +// member functions. Most of the functions here are small enough to be inlined +// anyway. Besides, you'll find that in actual practice you use less than 1/2 +// of the code here, even in big projects and different modules will use as +// little as 10% of it. That means a lot less functions actually get linked +// your binaries. If you export this code from a DLL, it ALL gets linked in. +// +// I've compared the size of the binaries from exporting vs NOT exporting. Take +// my word for it -- exporting this code is not worth the hassle. +// +// ----------------------------------------------------------------------------- +//#pragma warning(disable:4231) // non-standard extension ("extern template") +// SSDLLEXP template class SSDLLSPEC CStdStr<char>; +// SSDLLEXP template class SSDLLSPEC CStdStr<wchar_t>; + + +// ============================================================================= +// END OF CStdStr INLINE FUNCTION DEFINITIONS +// ============================================================================= + +// Now typedef our class names based upon this humongous template + +typedef CStdStr<char> CStdStringA; // a better std::string +typedef CStdStr<wchar_t> CStdStringW; // a better std::wstring +typedef CStdStr<uint16_t> CStdString16; // a 16bit char string +typedef CStdStr<uint32_t> CStdString32; // a 32bit char string +typedef CStdStr<OLECHAR> CStdStringO; // almost always CStdStringW + +// ----------------------------------------------------------------------------- +// CStdStr addition functions defined as inline +// ----------------------------------------------------------------------------- + + +inline CStdStringA operator+(const CStdStringA& s1, const CStdStringA& s2) +{ + CStdStringA sRet(SSREF(s1)); + sRet.append(s2); + return sRet; +} +inline CStdStringA operator+(const CStdStringA& s1, CStdStringA::value_type t) +{ + CStdStringA sRet(SSREF(s1)); + sRet.append(1, t); + return sRet; +} +inline CStdStringA operator+(const CStdStringA& s1, PCSTR pA) +{ + CStdStringA sRet(SSREF(s1)); + sRet.append(pA); + return sRet; +} +inline CStdStringA operator+(PCSTR pA, const CStdStringA& sA) +{ + CStdStringA sRet; + CStdStringA::size_type nObjSize = sA.size(); + CStdStringA::size_type nLitSize = + static_cast<CStdStringA::size_type>(sslen(pA)); + + sRet.reserve(nLitSize + nObjSize); + sRet.assign(pA); + sRet.append(sA); + return sRet; +} + + +inline CStdStringA operator+(const CStdStringA& s1, const CStdStringW& s2) +{ + return s1 + CStdStringA(s2); +} +inline CStdStringW operator+(const CStdStringW& s1, const CStdStringW& s2) +{ + CStdStringW sRet(SSREF(s1)); + sRet.append(s2); + return sRet; +} +inline CStdStringA operator+(const CStdStringA& s1, PCWSTR pW) +{ + return s1 + CStdStringA(pW); +} + +#ifdef UNICODE + inline CStdStringW operator+(PCWSTR pW, const CStdStringA& sA) + { + return CStdStringW(pW) + CStdStringW(SSREF(sA)); + } + inline CStdStringW operator+(PCSTR pA, const CStdStringW& sW) + { + return CStdStringW(pA) + sW; + } +#else + inline CStdStringA operator+(PCWSTR pW, const CStdStringA& sA) + { + return CStdStringA(pW) + sA; + } + inline CStdStringA operator+(PCSTR pA, const CStdStringW& sW) + { + return pA + CStdStringA(sW); + } +#endif + +// ...Now the wide string versions. +inline CStdStringW operator+(const CStdStringW& s1, CStdStringW::value_type t) +{ + CStdStringW sRet(SSREF(s1)); + sRet.append(1, t); + return sRet; +} +inline CStdStringW operator+(const CStdStringW& s1, PCWSTR pW) +{ + CStdStringW sRet(SSREF(s1)); + sRet.append(pW); + return sRet; +} +inline CStdStringW operator+(PCWSTR pW, const CStdStringW& sW) +{ + CStdStringW sRet; + CStdStringW::size_type nObjSize = sW.size(); + CStdStringA::size_type nLitSize = + static_cast<CStdStringW::size_type>(sslen(pW)); + + sRet.reserve(nLitSize + nObjSize); + sRet.assign(pW); + sRet.append(sW); + return sRet; +} + +inline CStdStringW operator+(const CStdStringW& s1, const CStdStringA& s2) +{ + return s1 + CStdStringW(s2); +} +inline CStdStringW operator+(const CStdStringW& s1, PCSTR pA) +{ + return s1 + CStdStringW(pA); +} + + +// New-style format function is a template + +#ifdef SS_SAFE_FORMAT + +template<> +struct FmtArg<CStdStringA> +{ + explicit FmtArg(const CStdStringA& arg) : a_(arg) {} + PCSTR operator()() const { return a_.c_str(); } + const CStdStringA& a_; +private: + FmtArg<CStdStringA>& operator=(const FmtArg<CStdStringA>&) { return *this; } +}; +template<> +struct FmtArg<CStdStringW> +{ + explicit FmtArg(const CStdStringW& arg) : a_(arg) {} + PCWSTR operator()() const { return a_.c_str(); } + const CStdStringW& a_; +private: + FmtArg<CStdStringW>& operator=(const FmtArg<CStdStringW>&) { return *this; } +}; + +template<> +struct FmtArg<std::string> +{ + explicit FmtArg(const std::string& arg) : a_(arg) {} + PCSTR operator()() const { return a_.c_str(); } + const std::string& a_; +private: + FmtArg<std::string>& operator=(const FmtArg<std::string>&) { return *this; } +}; +template<> +struct FmtArg<std::wstring> +{ + explicit FmtArg(const std::wstring& arg) : a_(arg) {} + PCWSTR operator()() const { return a_.c_str(); } + const std::wstring& a_; +private: + FmtArg<std::wstring>& operator=(const FmtArg<std::wstring>&) {return *this;} +}; +#endif // #ifdef SS_SAFEFORMAT + +#ifndef SS_ANSI + // SSResourceHandle: our MFC-like resource handle + inline HMODULE& SSResourceHandle() + { + static HMODULE hModuleSS = GetModuleHandle(0); + return hModuleSS; + } +#endif + + +// In MFC builds, define some global serialization operators +// Special operators that allow us to serialize CStdStrings to CArchives. +// Note that we use an intermediate CString object in order to ensure that +// we use the exact same format. + +#ifdef _MFC_VER + inline CArchive& AFXAPI operator<<(CArchive& ar, const CStdStringA& strA) + { + CString strTemp = strA; + return ar << strTemp; + } + inline CArchive& AFXAPI operator<<(CArchive& ar, const CStdStringW& strW) + { + CString strTemp = strW; + return ar << strTemp; + } + + inline CArchive& AFXAPI operator>>(CArchive& ar, CStdStringA& strA) + { + CString strTemp; + ar >> strTemp; + strA = strTemp; + return ar; + } + inline CArchive& AFXAPI operator>>(CArchive& ar, CStdStringW& strW) + { + CString strTemp; + ar >> strTemp; + strW = strTemp; + return ar; + } +#endif // #ifdef _MFC_VER -- (i.e. is this MFC?) + + + +// ----------------------------------------------------------------------------- +// GLOBAL FUNCTION: WUFormat +// CStdStringA WUFormat(UINT nId, ...); +// CStdStringA WUFormat(PCSTR szFormat, ...); +// +// REMARKS: +// This function allows the caller for format and return a CStdStringA +// object with a single line of code. +// ----------------------------------------------------------------------------- +#ifdef SS_ANSI +#else + inline CStdStringA WUFormatA(UINT nId, ...) + { + va_list argList; + va_start(argList, nId); + + CStdStringA strFmt; + CStdStringA strOut; + if ( strFmt.Load(nId) ) + strOut.FormatV(strFmt, argList); + + va_end(argList); + return strOut; + } + inline CStdStringA WUFormatA(PCSTR szFormat, ...) + { + va_list argList; + va_start(argList, szFormat); + CStdStringA strOut; + strOut.FormatV(szFormat, argList); + va_end(argList); + return strOut; + } + inline CStdStringW WUFormatW(UINT nId, ...) + { + va_list argList; + va_start(argList, nId); + + CStdStringW strFmt; + CStdStringW strOut; + if ( strFmt.Load(nId) ) + strOut.FormatV(strFmt, argList); + + va_end(argList); + return strOut; + } + inline CStdStringW WUFormatW(PCWSTR szwFormat, ...) + { + va_list argList; + va_start(argList, szwFormat); + CStdStringW strOut; + strOut.FormatV(szwFormat, argList); + va_end(argList); + return strOut; + } +#endif // #ifdef SS_ANSI + + + +#if defined(SS_WIN32) && !defined (SS_ANSI) + // ------------------------------------------------------------------------- + // FUNCTION: WUSysMessage + // CStdStringA WUSysMessageA(DWORD dwError, DWORD dwLangId=SS_DEFLANGID); + // CStdStringW WUSysMessageW(DWORD dwError, DWORD dwLangId=SS_DEFLANGID); + // + // DESCRIPTION: + // This function simplifies the process of obtaining a string equivalent + // of a system error code returned from GetLastError(). You simply + // supply the value returned by GetLastError() to this function and the + // corresponding system string is returned in the form of a CStdStringA. + // + // PARAMETERS: + // dwError - a DWORD value representing the error code to be translated + // dwLangId - the language id to use. defaults to english. + // + // RETURN VALUE: + // a CStdStringA equivalent of the error code. Currently, this function + // only returns either English of the system default language strings. + // ------------------------------------------------------------------------- + #define SS_DEFLANGID MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT) + inline CStdStringA WUSysMessageA(DWORD dwError, DWORD dwLangId=SS_DEFLANGID) + { + CHAR szBuf[512]; + + if ( 0 != ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, + dwLangId, szBuf, 511, NULL) ) + return WUFormatA("%s (0x%X)", szBuf, dwError); + else + return WUFormatA("Unknown error (0x%X)", dwError); + } + inline CStdStringW WUSysMessageW(DWORD dwError, DWORD dwLangId=SS_DEFLANGID) + { + WCHAR szBuf[512]; + + if ( 0 != ::FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, + dwLangId, szBuf, 511, NULL) ) + return WUFormatW(L"%s (0x%X)", szBuf, dwError); + else + return WUFormatW(L"Unknown error (0x%X)", dwError); + } +#endif + +// Define TCHAR based friendly names for some of these functions + +#ifdef UNICODE + //#define CStdString CStdStringW + typedef CStdStringW CStdString; + #define WUSysMessage WUSysMessageW + #define WUFormat WUFormatW +#else + //#define CStdString CStdStringA + typedef CStdStringA CStdString; + #define WUSysMessage WUSysMessageA + #define WUFormat WUFormatA +#endif + +// ...and some shorter names for the space-efficient + +#define WUSysMsg WUSysMessage +#define WUSysMsgA WUSysMessageA +#define WUSysMsgW WUSysMessageW +#define WUFmtA WUFormatA +#define WUFmtW WUFormatW +#define WUFmt WUFormat +#define WULastErrMsg() WUSysMessage(::GetLastError()) +#define WULastErrMsgA() WUSysMessageA(::GetLastError()) +#define WULastErrMsgW() WUSysMessageW(::GetLastError()) + + +// ----------------------------------------------------------------------------- +// FUNCTIONAL COMPARATORS: +// REMARKS: +// These structs are derived from the std::binary_function template. They +// give us functional classes (which may be used in Standard C++ Library +// collections and algorithms) that perform case-insensitive comparisons of +// CStdString objects. This is useful for maps in which the key may be the +// proper string but in the wrong case. +// ----------------------------------------------------------------------------- +#define StdStringLessNoCaseW SSLNCW // avoid VC compiler warning 4786 +#define StdStringEqualsNoCaseW SSENCW +#define StdStringLessNoCaseA SSLNCA +#define StdStringEqualsNoCaseA SSENCA + +#ifdef UNICODE + #define StdStringLessNoCase SSLNCW + #define StdStringEqualsNoCase SSENCW +#else + #define StdStringLessNoCase SSLNCA + #define StdStringEqualsNoCase SSENCA +#endif + +struct StdStringLessNoCaseW + : std::binary_function<CStdStringW, CStdStringW, bool> +{ + inline + bool operator()(const CStdStringW& sLeft, const CStdStringW& sRight) const + { return ssicmp(sLeft.c_str(), sRight.c_str()) < 0; } +}; +struct StdStringEqualsNoCaseW + : std::binary_function<CStdStringW, CStdStringW, bool> +{ + inline + bool operator()(const CStdStringW& sLeft, const CStdStringW& sRight) const + { return ssicmp(sLeft.c_str(), sRight.c_str()) == 0; } +}; +struct StdStringLessNoCaseA + : std::binary_function<CStdStringA, CStdStringA, bool> +{ + inline + bool operator()(const CStdStringA& sLeft, const CStdStringA& sRight) const + { return ssicmp(sLeft.c_str(), sRight.c_str()) < 0; } +}; +struct StdStringEqualsNoCaseA + : std::binary_function<CStdStringA, CStdStringA, bool> +{ + inline + bool operator()(const CStdStringA& sLeft, const CStdStringA& sRight) const + { return ssicmp(sLeft.c_str(), sRight.c_str()) == 0; } +}; + +// If we had to define our own version of TRACE above, get rid of it now + +#ifdef TRACE_DEFINED_HERE + #undef TRACE + #undef TRACE_DEFINED_HERE +#endif + + +// These std::swap specializations come courtesy of Mike Crusader. + +//namespace std +//{ +// inline void swap(CStdStringA& s1, CStdStringA& s2) throw() +// { +// s1.swap(s2); +// } +// template<> +// inline void swap(CStdStringW& s1, CStdStringW& s2) throw() +// { +// s1.swap(s2); +// } +//} + +// Turn back on any Borland warnings we turned off. + +#ifdef __BORLANDC__ + #pragma option pop // Turn back on inline function warnings +// #pragma warn +inl // Turn back on inline function warnings +#endif + +typedef std::vector<CStdString> CStdStringArray; + +#endif // #ifndef STDSTRING_H diff --git a/guilib/Texture.cpp b/guilib/Texture.cpp new file mode 100644 index 0000000000..89c16632c3 --- /dev/null +++ b/guilib/Texture.cpp @@ -0,0 +1,177 @@ +/* +* Copyright (C) 2005-2008 Team XBMC +* http://www.xbmc.org +* +* 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, 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 XBMC; see the file COPYING. If not, write to +* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +* http://www.gnu.org/copyleft/gpl.html +* +*/ + +#include "Texture.h" +#include "Picture.h" +#include "WindowingFactory.h" +#include "utils/log.h" + +#define MAX_PICTURE_WIDTH 2048 +#define MAX_PICTURE_HEIGHT 2048 + +/************************************************************************/ +/* */ +/************************************************************************/ +CBaseTexture::CBaseTexture(unsigned int width, unsigned int height, unsigned int BPP) +{ + m_imageWidth = width; + m_imageHeight = height; + m_pTexture = NULL; + m_pPixels = NULL; + m_nBPP = BPP; + m_loadedToGPU = false; + m_nTextureHeight = 0; + m_nTextureWidth = 0; +} + +CBaseTexture::~CBaseTexture() +{ + delete[] m_pPixels; +} + +void CBaseTexture::Allocate(unsigned int width, unsigned int height, unsigned int BPP) +{ + if (BPP != 0) + m_nBPP = BPP; + + m_imageWidth = width; + m_imageHeight = height; + + if (g_Windowing.NeedPower2Texture()) + { + m_nTextureWidth = PadPow2(m_imageWidth); + m_nTextureHeight = PadPow2(m_imageHeight); + } + else + { + m_nTextureWidth = m_imageWidth; + m_nTextureHeight = m_imageHeight; + } + + delete[] m_pPixels; + m_pPixels = new unsigned char[m_nTextureWidth * m_nTextureHeight * m_nBPP / 8]; +} + +void CBaseTexture::Update(int w, int h, int pitch, const unsigned char *pixels, bool loadToGPU) +{ + if (pixels == NULL) + return; + + Allocate(w, h, 0); + + // Resize texture to POT if needed + const unsigned char *src = pixels; + unsigned char* resized = m_pPixels; + + for (int y = 0; y < h; y++) + { + memcpy(resized, src, pitch); // make sure pitch is not bigger than our width + src += pitch; + + // repeat last column to simulate clamp_to_edge + for(unsigned int i = pitch; i < m_nTextureWidth*4; i+=4) + memcpy(resized+i, src-4, 4); + + resized += (m_nTextureWidth * 4); + } + + // clamp to edge - repeat last row + unsigned char *dest = m_pPixels + h*GetPitch(); + for (unsigned int y = h; y < m_nTextureHeight; y++) + { + memcpy(dest, dest-GetPitch(), GetPitch()); + dest += GetPitch(); + } + + if (loadToGPU) + LoadToGPU(); +} + +bool CBaseTexture::LoadFromFile(const CStdString& texturePath) +{ + CPicture pic; + if(!pic.Load(texturePath, this, MAX_PICTURE_WIDTH, MAX_PICTURE_HEIGHT)) + { + CLog::Log(LOGERROR, "Texture manager unable to load file: %s", texturePath.c_str()); + return false; + } + return true; +} + +bool CBaseTexture::LoadFromMemory(unsigned int width, unsigned int height, unsigned int pitch, unsigned int BPP, unsigned char* pPixels) +{ + m_imageWidth = width; + m_imageHeight = height; + m_nBPP = BPP; + Update(width, height, pitch, pPixels, false); + + return true; +} + +bool CBaseTexture::LoadPaletted(unsigned int width, unsigned int height, unsigned int pitch, const unsigned char *pixels, const COLOR *palette) +{ + if (pixels == NULL || palette == NULL) + return false; + + Allocate(width, height, 32); + + for (unsigned int y = 0; y < height; y++) + { + unsigned char *dest = m_pPixels + y * GetPitch(); + const unsigned char *src = pixels + y * pitch; + for (unsigned int x = 0; x < width; x++) + { + COLOR col = palette[*src++]; + *dest++ = col.b; + *dest++ = col.g; + *dest++ = col.r; + *dest++ = col.x; + } + // clamp to edge - repeat last column + for (unsigned int x = width; x < m_nTextureWidth; x++) + { + COLOR col = palette[*(src-1)]; + *dest++ = col.b; + *dest++ = col.g; + *dest++ = col.r; + *dest++ = col.x; + } + } + // clamp to edge - repeat last row + unsigned char *dest = m_pPixels + height*GetPitch(); + for (unsigned int y = height; y < m_nTextureHeight; y++) + { + memcpy(dest, dest-GetPitch(), GetPitch()); + dest += GetPitch(); + } + return true; +} + +DWORD CBaseTexture::PadPow2(DWORD x) +{ + --x; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return ++x; +} diff --git a/guilib/Texture.h b/guilib/Texture.h new file mode 100644 index 0000000000..16a481cbe8 --- /dev/null +++ b/guilib/Texture.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +/*! +\file Texture.h +\brief +*/ + +#ifndef GUILIB_TEXTURE_H +#define GUILIB_TEXTURE_H + +#include "gui3d.h" +#include "StdString.h" + +#pragma pack(1) +struct COLOR {unsigned char b,g,r,x;}; // Windows GDI expects 4bytes per color +#pragma pack() + +class CTexture; +class CGLTexture; +class CDXTexture; + +#pragma once + +/*! +\ingroup textures +\brief Base texture class, subclasses of which depend on the render spec (DX, GL etc.) +*/ +class CBaseTexture +{ + +public: + CBaseTexture(unsigned int width = 0, unsigned int height = 0, unsigned int BPP = 0); + virtual ~CBaseTexture(); + + // TODO: Clean up this interface once things have settled down (none of these need to be virtual) + virtual bool LoadFromFile(const CStdString& texturePath); + virtual bool LoadFromMemory(unsigned int width, unsigned int height, unsigned int pitch, unsigned int BPP, unsigned char* pPixels); + bool LoadPaletted(unsigned int width, unsigned int height, unsigned int pitch, const unsigned char *pixels, const COLOR *palette); + + virtual void CreateTextureObject() = 0; + virtual void DestroyTextureObject() = 0; + virtual void LoadToGPU() = 0; + + XBMC::TexturePtr GetTextureObject() const { return m_pTexture; } + virtual unsigned char* GetPixels() const { return m_pPixels; } + virtual unsigned int GetPitch() const { return m_nTextureWidth * (m_nBPP / 8); } + virtual unsigned int GetTextureWidth() const { return m_nTextureWidth; }; + virtual unsigned int GetTextureHeight() const { return m_nTextureHeight; }; + unsigned int GetWidth() const { return m_imageWidth; } + unsigned int GetHeight() const { return m_imageHeight; } + unsigned int GetBPP() const { return m_nBPP; } + + void Update(int w, int h, int pitch, const unsigned char *pixels, bool loadToGPU); + void Allocate(unsigned int width, unsigned int height, unsigned int BPP); + + static DWORD PadPow2(DWORD x); + +protected: + unsigned int m_imageWidth; + unsigned int m_imageHeight; + unsigned int m_nTextureWidth; + unsigned int m_nTextureHeight; + unsigned int m_nBPP; + XBMC::TexturePtr m_pTexture; + unsigned char* m_pPixels; + bool m_loadedToGPU; +}; + +#if defined(HAS_GL) || defined(HAS_GLES) +#include "TextureGL.h" +#define CTexture CGLTexture +#elif defined(HAS_DX) +#include "TextureDX.h" +#define CTexture CDXTexture +#endif + +#endif diff --git a/guilib/TextureBundle.cpp b/guilib/TextureBundle.cpp new file mode 100644 index 0000000000..6d7176c9a6 --- /dev/null +++ b/guilib/TextureBundle.cpp @@ -0,0 +1,492 @@ + + +#include "system.h" +#include "TextureBundle.h" +#include "Texture.h" +#include "GraphicContext.h" +#include "DirectXGraphics.h" +#include "utils/log.h" +#ifndef _LINUX +#include <sys/stat.h> +#include "utils/CharsetConverter.h" +#include "lib/liblzo/LZO1X.H" +#else +#include <lzo/lzo1x.h> +#endif +#include "SkinInfo.h" +#include "GUISettings.h" +#include "Util.h" +#include "FileSystem/SpecialProtocol.h" +#include "utils/EndianSwap.h" + +#if !defined(__GNUC__) +#pragma comment(lib,"../../xbmc/lib/liblzo/lzo.lib") +#endif + +// alignment of file blocks - should be a multiple of the sector size of the disk and a power of 2 +// HDD sector = 512 bytes, DVD/CD sector = 2048 bytes +#undef ALIGN +#define ALIGN (512) + + +enum XPR_FLAGS +{ + XPRFLAG_PALETTE = 0x00000001, + XPRFLAG_ANIM = 0x00000002 +}; + +class CAutoBuffer +{ + BYTE* p; +public: + CAutoBuffer() { p = 0; } + explicit CAutoBuffer(size_t s) { p = (BYTE*)malloc(s); } + ~CAutoBuffer() { free(p); } +operator BYTE*() { return p; } + void Set(BYTE* buf) { free(p); p = buf; } + bool Resize(size_t s); +void Release() { p = 0; } +}; + +bool CAutoBuffer::Resize(size_t s) +{ + if (s == 0) + { + if (!p) + return false; + free(p); + p = 0; + return true; + } + void* q = realloc(p, s); + if (q) + { + p = (BYTE*)q; + return true; + } + return false; +} + +// as above but for texture allocation (do not change from XPhysicalAlloc!) +class CAutoTexBuffer +{ + BYTE* p; +public: + CAutoTexBuffer() { p = 0; } + explicit CAutoTexBuffer(size_t s) { p = (BYTE*)XPhysicalAlloc(s, MAXULONG_PTR, 128, PAGE_READWRITE); } + ~CAutoTexBuffer() { if (p) XPhysicalFree(p); } +operator BYTE*() { return p; } + BYTE* Set(BYTE* buf) { if (p) XPhysicalFree(p); return p = buf; } +void Release() { p = 0; } +}; + +CTextureBundle::CTextureBundle(void) +{ + m_hFile = NULL; + m_themeBundle = false; +} + +CTextureBundle::~CTextureBundle(void) +{ + if (m_hFile != NULL) + fclose(m_hFile); +} + +bool CTextureBundle::OpenBundle() +{ + DWORD AlignedSize; + DWORD HeaderSize; + int Version; + XPR_HEADER* pXPRHeader; + + if (m_hFile != NULL) + Cleanup(); + + CStdString strPath; + + if (m_themeBundle) + { + // if we are the theme bundle, we only load if the user has chosen + // a valid theme (or the skin has a default one) + CStdString themeXPR = g_guiSettings.GetString("lookandfeel.skintheme"); + if (!themeXPR.IsEmpty() && themeXPR.CompareNoCase("SKINDEFAULT")) + { + strPath = CUtil::AddFileToFolder(g_graphicsContext.GetMediaDir(), "media"); + strPath = CUtil::AddFileToFolder(strPath, themeXPR); + } + else + return false; + } + else + strPath = CUtil::AddFileToFolder(g_graphicsContext.GetMediaDir(), "media/Textures.xpr"); + + strPath = PTH_IC(strPath); + +#ifndef _LINUX + CStdStringW strPathW; + g_charsetConverter.utf8ToW(_P(strPath), strPathW, false); + m_hFile = _wfopen(strPathW.c_str(), L"rb"); +#else + m_hFile = fopen(strPath.c_str(), "rb"); +#endif + if (m_hFile == NULL) + return false; + + struct stat fileStat; + if (fstat(fileno(m_hFile), &fileStat) == -1) + return false; + m_TimeStamp = fileStat.st_mtime; + + CAutoBuffer HeaderBuf(ALIGN); + DWORD n; + + n = fread(HeaderBuf, 1, ALIGN, m_hFile); + if (n < ALIGN) + goto LoadError; + + pXPRHeader = (XPR_HEADER*)(BYTE*)HeaderBuf; + pXPRHeader->dwMagic = Endian_SwapLE32(pXPRHeader->dwMagic); + Version = (pXPRHeader->dwMagic >> 24) - '0'; + pXPRHeader->dwMagic -= Version << 24; + Version &= 0x0f; + + if (pXPRHeader->dwMagic != XPR_MAGIC_VALUE || Version < 2) + goto LoadError; + + HeaderSize = Endian_SwapLE32(pXPRHeader->dwHeaderSize); + AlignedSize = (HeaderSize - 1) & ~(ALIGN - 1); // align to sector, but remove the first sector + HeaderBuf.Resize(AlignedSize + ALIGN); + + if (fseek(m_hFile, ALIGN, SEEK_SET) == -1) + goto LoadError; + n = fread(HeaderBuf + ALIGN, 1, AlignedSize, m_hFile); + if (n < ALIGN) + goto LoadError; + + struct DiskFileHeader_t + { + char Name[116]; + DWORD Offset; + DWORD UnpackedSize; + DWORD PackedSize; + } + *FileHeader; + FileHeader = (DiskFileHeader_t*)(HeaderBuf + sizeof(XPR_HEADER)); + + n = (HeaderSize - sizeof(XPR_HEADER)) / sizeof(DiskFileHeader_t); + for (unsigned i = 0; i < n; ++i) + { + std::pair<CStdString, FileHeader_t> entry; + entry.first = Normalize(FileHeader[i].Name); + entry.second.Offset = Endian_SwapLE32(FileHeader[i].Offset); + entry.second.UnpackedSize = Endian_SwapLE32(FileHeader[i].UnpackedSize); + entry.second.PackedSize = Endian_SwapLE32(FileHeader[i].PackedSize); + m_FileHeaders.insert(entry); + } + + if (lzo_init() != LZO_E_OK) + goto LoadError; + + return true; + +LoadError: + CLog::Log(LOGERROR, "Unable to load file: %s: %s", strPath.c_str(), strerror(errno)); + fclose(m_hFile); + m_hFile = NULL; + + return false; +} + +void CTextureBundle::Cleanup() +{ + if (m_hFile != NULL) + fclose(m_hFile); + m_hFile = NULL; + + m_FileHeaders.clear(); +} + +bool CTextureBundle::HasFile(const CStdString& Filename) +{ + if (m_hFile == NULL && !OpenBundle()) + return false; + + struct stat fileStat; + if (fstat(fileno(m_hFile), &fileStat) == -1) + return false; + if (fileStat.st_mtime > m_TimeStamp) + { + CLog::Log(LOGINFO, "Texture bundle has changed, reloading"); + Cleanup(); + if (!OpenBundle()) + return false; + } + + CStdString name = Normalize(Filename); + return m_FileHeaders.find(name) != m_FileHeaders.end(); +} + +void CTextureBundle::GetTexturesFromPath(const CStdString &path, std::vector<CStdString> &textures) +{ + if (path.GetLength() > 1 && path[1] == ':') + return; + + if (m_hFile == NULL && !OpenBundle()) + return; + + CStdString testPath = Normalize(path); + if (!CUtil::HasSlashAtEnd(testPath)) + testPath += "\\"; + int testLength = testPath.GetLength(); + std::map<CStdString, FileHeader_t>::iterator it; + for (it = m_FileHeaders.begin(); it != m_FileHeaders.end(); it++) + { + if (it->first.Left(testLength).Equals(testPath)) + textures.push_back(it->first); + } +} + +HRESULT CTextureBundle::LoadFile(const CStdString& Filename, CAutoTexBuffer& UnpackedBuf) +{ + if (Filename == "-") + return 0; + + CStdString name = Normalize(Filename); + + std::map<CStdString, FileHeader_t>::iterator file = m_FileHeaders.find(name); + if (file == m_FileHeaders.end()) + return E_FAIL; + + // found texture - allocate the necessary buffers + DWORD ReadSize = (file->second.PackedSize + (ALIGN - 1)) & ~(ALIGN - 1); + BYTE *buffer = (BYTE*)malloc(ReadSize); + + if (!buffer || !UnpackedBuf.Set((BYTE*)XPhysicalAlloc(file->second.UnpackedSize, MAXULONG_PTR, 128, PAGE_READWRITE))) + { // failed due to lack of memory +#ifndef _LINUX + MEMORYSTATUS stat; + GlobalMemoryStatus(&stat); + CLog::Log(LOGERROR, "Out of memory loading texture: %s (need %lu bytes, have %lu bytes)", name.c_str(), + file->second.UnpackedSize + file->second.PackedSize, stat.dwAvailPhys); +#elif defined(__APPLE__) + CLog::Log(LOGERROR, "Out of memory loading texture: %s (need %lu bytes)", name.c_str(), + file->second.UnpackedSize + file->second.PackedSize); +#else + struct sysinfo info; + sysinfo(&info); + CLog::Log(LOGERROR, "Out of memory loading texture: %s " + "(need %u bytes, have %lu bytes)", + name.c_str(), file->second.UnpackedSize + file->second.PackedSize, + info.totalram); +#endif + free(buffer); + return E_OUTOFMEMORY; + } + + // read the file into our buffer + DWORD n; + fseek(m_hFile, file->second.Offset, SEEK_SET); + n = fread(buffer, 1, ReadSize, m_hFile); + if (n < ReadSize && !feof(m_hFile)) + { + CLog::Log(LOGERROR, "Error loading texture: %s: %s", Filename.c_str(), strerror(ferror(m_hFile))); + free(buffer); + return E_FAIL; + } + + // allocate a buffer for our unpacked texture + lzo_uint s = file->second.UnpackedSize; + HRESULT hr = S_OK; + if (lzo1x_decompress(buffer, file->second.PackedSize, UnpackedBuf, &s, NULL) != LZO_E_OK || + s != file->second.UnpackedSize) + { + CLog::Log(LOGERROR, "Error loading texture: %s: Decompression error", Filename.c_str()); + hr = E_FAIL; + } + + try + { + free(buffer); + } + catch (...) + { + CLog::Log(LOGERROR, "Error freeing preload buffer."); + } + + return hr; +} + +HRESULT CTextureBundle::LoadTexture(const CStdString& Filename, CBaseTexture** ppTexture, + int &width, int &height) +{ + DWORD ResDataOffset; + *ppTexture = NULL; + + CAutoTexBuffer UnpackedBuf; + HRESULT r = LoadFile(Filename, UnpackedBuf); + if (r != S_OK) + return r; + + D3DTexture *pTex = (D3DTexture *)(new char[sizeof (D3DTexture)]); + D3DPalette* pPal = 0; + void* ResData = 0; + + WORD RealSize[2]; + + enum XPR_FLAGS + { + XPRFLAG_PALETTE = 0x00000001, + XPRFLAG_ANIM = 0x00000002 + }; + + BYTE* Next = UnpackedBuf; + + DWORD flags = Endian_SwapLE32(*(DWORD*)Next); + Next += sizeof(DWORD); + if ((flags & XPRFLAG_ANIM) || (flags >> 16) > 1) + goto PackedLoadError; + + if (flags & XPRFLAG_PALETTE) + Next += sizeof(D3DPalette); + + memcpy(pTex, Next, sizeof(D3DTexture)); + pTex->Common = Endian_SwapLE32(pTex->Common); + pTex->Data = Endian_SwapLE32(pTex->Data); + pTex->Lock = Endian_SwapLE32(pTex->Lock); + pTex->Format = Endian_SwapLE32(pTex->Format); + pTex->Size = Endian_SwapLE32(pTex->Size); + Next += sizeof(D3DTexture); + + memcpy(RealSize, Next, 4); + Next += 4; + + ResDataOffset = ((Next - UnpackedBuf) + 127) & ~127; + ResData = UnpackedBuf + ResDataOffset; + + if ((pTex->Common & D3DCOMMON_TYPE_MASK) != D3DCOMMON_TYPE_TEXTURE) + goto PackedLoadError; + + GetTextureFromData(pTex, ResData, ppTexture); + delete[] pTex; + + width = Endian_SwapLE16(RealSize[0]); + height = Endian_SwapLE16(RealSize[1]); +/* DXMERGE - this was previously used to specify the format of the image - probably only affects directx? +#ifndef HAS_SDL + D3DSURFACE_DESC desc; + (*ppTexture)->GetLevelDesc(0, &desc); + pInfo->Format = desc.Format; +#endif +*/ + return S_OK; + +PackedLoadError: + CLog::Log(LOGERROR, "Error loading texture: %s: Invalid data", Filename.c_str()); + delete[] pTex; + delete pPal; + return E_FAIL; +} + +int CTextureBundle::LoadAnim(const CStdString& Filename, CBaseTexture*** ppTextures, + int &width, int &height, int& nLoops, int** ppDelays) +{ + DWORD ResDataOffset; + int nTextures = 0; + + *ppTextures = NULL; *ppDelays = NULL; + + CAutoTexBuffer UnpackedBuf; + HRESULT r = LoadFile(Filename, UnpackedBuf); + if (r != S_OK) + return 0; + + struct AnimInfo_t + { + DWORD nLoops; + WORD RealSize[2]; + } + *pAnimInfo; + + D3DTexture** ppTex = 0; + void* ResData = 0; + + BYTE* Next = UnpackedBuf; + + DWORD flags = Endian_SwapLE32(*(DWORD*)Next); + Next += sizeof(DWORD); + if (!(flags & XPRFLAG_ANIM)) + goto PackedAnimError; + + pAnimInfo = (AnimInfo_t*)Next; + Next += sizeof(AnimInfo_t); + nLoops = Endian_SwapLE32(pAnimInfo->nLoops); + + if (flags & XPRFLAG_PALETTE) + Next += sizeof(D3DPalette); + + nTextures = flags >> 16; + ppTex = new D3DTexture * [nTextures]; + *ppDelays = new int[nTextures]; + for (int i = 0; i < nTextures; ++i) + { + ppTex[i] = (D3DTexture *)(new char[sizeof (D3DTexture)+ sizeof (DWORD)]); + + memcpy(ppTex[i], Next, sizeof(D3DTexture)); + ppTex[i]->Common = Endian_SwapLE32(ppTex[i]->Common); + ppTex[i]->Data = Endian_SwapLE32(ppTex[i]->Data); + ppTex[i]->Lock = Endian_SwapLE32(ppTex[i]->Lock); + ppTex[i]->Format = Endian_SwapLE32(ppTex[i]->Format); + ppTex[i]->Size = Endian_SwapLE32(ppTex[i]->Size); + Next += sizeof(D3DTexture); + + (*ppDelays)[i] = Endian_SwapLE32(*(int*)Next); + Next += sizeof(int); + } + + ResDataOffset = ((DWORD)(Next - UnpackedBuf) + 127) & ~127; + ResData = UnpackedBuf + ResDataOffset; + + *ppTextures = new CBaseTexture*[nTextures]; + for (int i = 0; i < nTextures; ++i) + { + if ((ppTex[i]->Common & D3DCOMMON_TYPE_MASK) != D3DCOMMON_TYPE_TEXTURE) + goto PackedAnimError; + + GetTextureFromData(ppTex[i], ResData, &(*ppTextures)[i]); + delete[] ppTex[i]; + } + + delete[] ppTex; + ppTex = 0; + + width = Endian_SwapLE16(pAnimInfo->RealSize[0]); + height = Endian_SwapLE16(pAnimInfo->RealSize[1]); + + return nTextures; + +PackedAnimError: + CLog::Log(LOGERROR, "Error loading texture: %s: Invalid data", Filename.c_str()); + if (ppTex) + { + for (int i = 0; i < nTextures; ++i) + delete [] ppTex[i]; + delete [] ppTex; + } + delete [] *ppDelays; + return 0; +} + +void CTextureBundle::SetThemeBundle(bool themeBundle) +{ + m_themeBundle = themeBundle; +} + +// normalize to how it's stored within the bundle +// lower case + using \\ rather than / +CStdString CTextureBundle::Normalize(const CStdString &name) +{ + CStdString newName(name); + newName.Normalize(); + newName.Replace('/','\\'); + return newName; +} diff --git a/guilib/TextureBundle.h b/guilib/TextureBundle.h new file mode 100644 index 0000000000..c538d760a3 --- /dev/null +++ b/guilib/TextureBundle.h @@ -0,0 +1,70 @@ +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "StdString.h" +#include "system.h" + +#include <map> + +class CAutoTexBuffer; +class CBaseTexture; + +class CTextureBundle +{ + struct FileHeader_t + { + DWORD Offset; + DWORD UnpackedSize; + DWORD PackedSize; + }; + + FILE* m_hFile; + time_t m_TimeStamp; + + std::map<CStdString, FileHeader_t> m_FileHeaders; + typedef std::map<CStdString, FileHeader_t>::iterator iFiles; + + bool m_themeBundle; + + bool OpenBundle(); + HRESULT LoadFile(const CStdString& Filename, CAutoTexBuffer& UnpackedBuf); + +public: + CTextureBundle(void); + ~CTextureBundle(void); + + void Cleanup(); + + void SetThemeBundle(bool themeBundle); + bool HasFile(const CStdString& Filename); + void GetTexturesFromPath(const CStdString &path, std::vector<CStdString> &textures); + static CStdString Normalize(const CStdString &name); + + HRESULT LoadTexture(const CStdString& Filename, CBaseTexture** ppTexture, + int &width, int &height); + + int LoadAnim(const CStdString& Filename, CBaseTexture*** ppTextures, + int &width, int &height, int& nLoops, int** ppDelays); +}; + + diff --git a/guilib/TextureDX.cpp b/guilib/TextureDX.cpp new file mode 100644 index 0000000000..7ed3c05816 --- /dev/null +++ b/guilib/TextureDX.cpp @@ -0,0 +1,105 @@ +/* +* Copyright (C) 2005-2008 Team XBMC +* http://www.xbmc.org +* +* 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, 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 XBMC; see the file COPYING. If not, write to +* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +* http://www.gnu.org/copyleft/gpl.html +* +*/ + +#include "TextureDX.h" +#include "WindowingFactory.h" +#include "../xbmc/FileSystem/SpecialProtocol.h" +#include "utils/log.h" + +#ifdef HAS_DX + +/************************************************************************/ +/* CDXTexture */ +/************************************************************************/ +CDXTexture::CDXTexture(unsigned int width, unsigned int height, unsigned int BPP) +: CBaseTexture(width, height, BPP) +{ + Allocate(m_imageWidth, m_imageHeight, m_nBPP); +} + +CDXTexture::~CDXTexture() +{ + DestroyTextureObject(); +} + +void CDXTexture::CreateTextureObject() +{ + D3DFORMAT format; + + if (m_nBPP == 8) + format = D3DFMT_LIN_A8; + else if (m_nBPP == 32) + format = D3DFMT_LIN_A8R8G8B8; + else + return; + + SAFE_RELEASE(m_pTexture); + + D3DXCreateTexture(g_Windowing.Get3DDevice(), m_nTextureWidth, m_nTextureHeight, 1, 0, format, D3DPOOL_MANAGED , &m_pTexture); +} + +void CDXTexture::DestroyTextureObject() +{ + SAFE_RELEASE(m_pTexture); +} + +void CDXTexture::LoadToGPU() +{ + if (!m_pPixels) + { + // nothing to load - probably same image (no change) + return; + } + + if (m_pTexture == NULL) + { + CreateTextureObject(); + if (m_pTexture == NULL) + { + CLog::Log(LOGDEBUG, "CDXTexture::CDXTexture: Error creating new texture for size %d x %d", m_nTextureWidth, m_nTextureHeight); + return; + } + } + + D3DLOCKED_RECT lr; + if ( D3D_OK == m_pTexture->LockRect( 0, &lr, NULL, 0 )) + { + DWORD destPitch = lr.Pitch; + + DWORD srcPitch = m_imageWidth * m_nBPP / 8; + BYTE *pixels = (BYTE *)lr.pBits; + + for (unsigned int y = 0; y < m_nTextureHeight; y++) + { + BYTE *dst = pixels + y * destPitch; + BYTE *src = m_pPixels + y * srcPitch; + memcpy(dst, src, srcPitch); + } + } + m_pTexture->UnlockRect(0); + + delete [] m_pPixels; + m_pPixels = NULL; + + m_loadedToGPU = true; +} + +#endif diff --git a/guilib/TextureDX.h b/guilib/TextureDX.h new file mode 100644 index 0000000000..db49a9cbd7 --- /dev/null +++ b/guilib/TextureDX.h @@ -0,0 +1,52 @@ +/* +* Copyright (C) 2005-2008 Team XBMC +* http://www.xbmc.org +* +* 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, 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 XBMC; see the file COPYING. If not, write to +* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +* http://www.gnu.org/copyleft/gpl.html +* +*/ + +/*! +\file TextureDX.h +\brief +*/ + +#ifndef GUILIB_TEXTUREDX_H +#define GUILIB_TEXTUREDX_H + +#include "Texture.h" + +#pragma once + +#ifdef HAS_DX + +/************************************************************************/ +/* CDXTexture */ +/************************************************************************/ +class CDXTexture : public CBaseTexture +{ +public: + CDXTexture(unsigned int width = 0, unsigned int height = 0, unsigned int BPP = 0); + virtual ~CDXTexture(); + + void CreateTextureObject(); + void DestroyTextureObject(); + virtual void LoadToGPU(); +}; + +#endif + +#endif diff --git a/guilib/TextureGL.cpp b/guilib/TextureGL.cpp new file mode 100644 index 0000000000..af4422a0d1 --- /dev/null +++ b/guilib/TextureGL.cpp @@ -0,0 +1,116 @@ +/* +* Copyright (C) 2005-2008 Team XBMC +* http://www.xbmc.org +* +* 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, 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 XBMC; see the file COPYING. If not, write to +* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +* http://www.gnu.org/copyleft/gpl.html +* +*/ + +#include "system.h" +#include "TextureGL.h" +#include "WindowingFactory.h" +#include "utils/log.h" + +#if defined(HAS_GL) || defined(HAS_GLES) + +using namespace std; + +/************************************************************************/ +/* CGLTexture */ +/************************************************************************/ +CGLTexture::CGLTexture(unsigned int width, unsigned int height, unsigned int BPP) +: CBaseTexture(width, height, BPP) +{ + m_nTextureWidth = 0; + m_nTextureHeight = 0; + + if(m_imageWidth != 0 && m_imageHeight != 0) + Allocate(m_imageWidth, m_imageHeight, m_nBPP); +} + +CGLTexture::~CGLTexture() +{ + DestroyTextureObject(); +} + +void CGLTexture::CreateTextureObject() +{ + glGenTextures(1, (GLuint*) &m_pTexture); +} + +void CGLTexture::DestroyTextureObject() +{ + if (m_pTexture) + glDeleteTextures(1, (GLuint*) &m_pTexture); +} + +void CGLTexture::LoadToGPU() +{ + if (!m_pPixels) + { + // nothing to load - probably same image (no change) + return; + } + + if (m_pTexture == 0) + { + // Have OpenGL generate a texture object handle for us + // this happens only one time - the first time the texture is loaded + CreateTextureObject(); + } + + // Bind the texture object + glBindTexture(GL_TEXTURE_2D, m_pTexture); + + // Set the texture's stretching properties + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + unsigned int maxSize = g_Windowing.GetMaxTextureSize(); + if (m_nTextureHeight > maxSize) + { + CLog::Log(LOGERROR, "GL: Image height %d too big to fit into single texture unit, truncating to %u", m_nTextureHeight, maxSize); + m_nTextureHeight = maxSize; + } + if (m_nTextureWidth > maxSize) + { + CLog::Log(LOGERROR, "GL: Image width %d too big to fit into single texture unit, truncating to %u", m_nTextureWidth, maxSize); +#ifndef HAS_GLES + glPixelStorei(GL_UNPACK_ROW_LENGTH, m_nTextureWidth); +#endif + m_nTextureWidth = maxSize; + } + +#ifdef HAS_GL + GLenum format = GL_BGRA; +#elif HAS_GLES + GLenum format = GL_BGRA_EXT; +#endif + glTexImage2D(GL_TEXTURE_2D, 0, 4, m_nTextureWidth, m_nTextureHeight, 0, + format, GL_UNSIGNED_BYTE, m_pPixels); +#ifndef HAS_GLES + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); +#endif + VerifyGLState(); + + delete [] m_pPixels; + m_pPixels = NULL; + + m_loadedToGPU = true; +} +#endif // HAS_GL diff --git a/guilib/TextureGL.h b/guilib/TextureGL.h new file mode 100644 index 0000000000..962c6f462d --- /dev/null +++ b/guilib/TextureGL.h @@ -0,0 +1,52 @@ +/* +* Copyright (C) 2005-2008 Team XBMC +* http://www.xbmc.org +* +* 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, 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 XBMC; see the file COPYING. If not, write to +* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +* http://www.gnu.org/copyleft/gpl.html +* +*/ + +/*! +\file TextureManager.h +\brief +*/ + +#ifndef GUILIB_TEXTUREGL_H +#define GUILIB_TEXTUREGL_H + +#include "Texture.h" + +#pragma once + +#if defined(HAS_GL) || defined(HAS_GLES) + +/************************************************************************/ +/* CGLTexture */ +/************************************************************************/ +class CGLTexture : public CBaseTexture +{ +public: + CGLTexture(unsigned int width = 0, unsigned int height = 0, unsigned int BPP = 0); + virtual ~CGLTexture(); + + void CreateTextureObject(); + virtual void DestroyTextureObject(); + void LoadToGPU(); +}; + +#endif + +#endif diff --git a/guilib/TextureManager.cpp b/guilib/TextureManager.cpp new file mode 100644 index 0000000000..22191b1e2c --- /dev/null +++ b/guilib/TextureManager.cpp @@ -0,0 +1,579 @@ +/* +* Copyright (C) 2005-2008 Team XBMC +* http://www.xbmc.org +* +* 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, 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 XBMC; see the file COPYING. If not, write to +* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +* http://www.gnu.org/copyleft/gpl.html +* +*/ + +#include "TextureManager.h" +#include "Texture.h" +#include "AnimatedGif.h" +#include "GraphicContext.h" +#include "utils/SingleLock.h" +#include "utils/CharsetConverter.h" +#include "utils/log.h" +#include "../xbmc/Util.h" +#include "../xbmc/FileSystem/File.h" +#include "../xbmc/FileSystem/Directory.h" +#include <assert.h> + +using namespace std; + +CGUITextureManager g_TextureManager; + +/************************************************************************/ +/* */ +/************************************************************************/ +CTextureArray::CTextureArray(int width, int height, int loops, bool texCoordsArePixels) +{ + m_width = width; + m_height = height; + m_loops = loops; + m_texWidth = 0; + m_texHeight = 0; + m_texCoordsArePixels = false; +} + +CTextureArray::CTextureArray() +{ + Reset(); +} + +CTextureArray::~CTextureArray() +{ + +} + +unsigned int CTextureArray::size() const +{ + return m_textures.size(); +} + + +void CTextureArray::Reset() +{ + m_textures.clear(); + m_delays.clear(); + m_width = 0; + m_height = 0; + m_loops = 0; + m_texWidth = 0; + m_texHeight = 0; + m_texCoordsArePixels = false; +} + +void CTextureArray::Add(CBaseTexture *texture, int delay) +{ + if (!texture) + return; + + m_textures.push_back(texture); + m_delays.push_back(delay ? delay * 2 : 100); + + m_texWidth = texture->GetTextureWidth(); + m_texHeight = texture->GetTextureHeight(); + m_texCoordsArePixels = false; +} + +void CTextureArray::Set(CBaseTexture *texture, int width, int height) +{ + assert(!m_textures.size()); // don't try and set a texture if we already have one! + m_width = width; + m_height = height; + Add(texture, 100); +} + +void CTextureArray::Free() +{ + CSingleLock lock(g_graphicsContext); + for (unsigned int i = 0; i < m_textures.size(); i++) + { + delete m_textures[i]; + } + + m_textures.clear(); + m_delays.clear(); + + Reset(); +} + + +/************************************************************************/ +/* */ +/************************************************************************/ + +CTextureMap::CTextureMap() +{ + m_textureName = ""; + m_referenceCount = 0; + m_memUsage = 0; +} + +CTextureMap::CTextureMap(const CStdString& textureName, int width, int height, int loops) +: m_texture(width, height, loops) +{ + m_textureName = textureName; + m_referenceCount = 0; + m_memUsage = 0; +} + +CTextureMap::~CTextureMap() +{ + FreeTexture(); +} + +bool CTextureMap::Release() +{ + if (!m_texture.m_textures.size()) + return true; + if (!m_referenceCount) + return true; + + m_referenceCount--; + if (!m_referenceCount) + { + return true; + } + return false; +} + +const CStdString& CTextureMap::GetName() const +{ + return m_textureName; +} + +const CTextureArray& CTextureMap::GetTexture() +{ + m_referenceCount++; + return m_texture; +} + +void CTextureMap::Dump() const +{ + if (!m_referenceCount) + return; // nothing to see here + + CStdString strLog; + strLog.Format(" texture:%s has %i frames %i refcount\n", m_textureName.c_str(), m_texture.m_textures.size(), m_referenceCount); + OutputDebugString(strLog.c_str()); +} + +DWORD CTextureMap::GetMemoryUsage() const +{ + return m_memUsage; +} + +void CTextureMap::Flush() +{ + if (!m_referenceCount) + FreeTexture(); +} + + +void CTextureMap::FreeTexture() +{ + m_texture.Free(); +} + +bool CTextureMap::IsEmpty() const +{ + return m_texture.m_textures.size() == 0; +} + +void CTextureMap::Add(CBaseTexture* texture, int delay) +{ + m_texture.Add(texture, delay); + + if (texture) + m_memUsage += sizeof(CTexture) + (texture->GetTextureWidth() * texture->GetTextureHeight() * 4); +} + +/************************************************************************/ +/* */ +/************************************************************************/ +CGUITextureManager::CGUITextureManager(void) +{ + // we set the theme bundle to be the first bundle (thus prioritizing it) + m_TexBundle[0].SetThemeBundle(true); +} + +CGUITextureManager::~CGUITextureManager(void) +{ + Cleanup(); +} + +const CTextureArray& CGUITextureManager::GetTexture(const CStdString& strTextureName) +{ + static CTextureArray emptyTexture; + // CLog::Log(LOGINFO, " refcount++ for GetTexture(%s)\n", strTextureName.c_str()); + for (int i = 0; i < (int)m_vecTextures.size(); ++i) + { + CTextureMap *pMap = m_vecTextures[i]; + if (pMap->GetName() == strTextureName) + { + //CLog::Log(LOGDEBUG, "Total memusage %u", GetMemoryUsage()); + return pMap->GetTexture(); + } + } + return emptyTexture; +} + + + + +/************************************************************************/ +/* */ +/************************************************************************/ +bool CGUITextureManager::CanLoad(const CStdString &texturePath) const +{ + if (texturePath == "-") + return false; + + if (!CURL::IsFullPath(texturePath)) + return true; // assume we have it + + // we can't (or shouldn't) be loading from remote paths, so check these + return CUtil::IsHD(texturePath); +} + +bool CGUITextureManager::HasTexture(const CStdString &textureName, CStdString *path, int *bundle, int *size) +{ + // default values + if (bundle) *bundle = -1; + if (size) *size = 0; + if (path) *path = textureName; + + if (!CanLoad(textureName)) + return false; + + // Check our loaded and bundled textures - we store in bundles using \\. + CStdString bundledName = CTextureBundle::Normalize(textureName); + for (int i = 0; i < (int)m_vecTextures.size(); ++i) + { + CTextureMap *pMap = m_vecTextures[i]; + if (pMap->GetName() == textureName) + { + if (size) *size = 1; + return true; + } + } + + for (int i = 0; i < 2; i++) + { + if (m_TexBundle[i].HasFile(bundledName)) + { + if (bundle) *bundle = i; + return true; + } + } + + CStdString fullPath = GetTexturePath(textureName); + if (path) + *path = fullPath; + + return !fullPath.IsEmpty(); +} + +int CGUITextureManager::Load(const CStdString& strTextureName, bool checkBundleOnly /*= false */) +{ + CStdString strPath; + int bundle = -1; + int size = 0; + if (!HasTexture(strTextureName, &strPath, &bundle, &size)) + return 0; + + if (size) // we found the texture + return size; + + if (checkBundleOnly && bundle == -1) + return 0; + + //Lock here, we will do stuff that could break rendering + CSingleLock lock(g_graphicsContext); + +#ifdef _DEBUG + LARGE_INTEGER start; + QueryPerformanceCounter(&start); +#endif + + if (strPath.Right(4).ToLower() == ".gif") + { + CTextureMap* pMap; + + if (bundle >= 0) + { + CBaseTexture **pTextures; + int nLoops = 0, width = 0, height = 0; + int* Delay; + int nImages = m_TexBundle[bundle].LoadAnim(strTextureName, &pTextures, width, height, nLoops, &Delay); + if (!nImages) + { + CLog::Log(LOGERROR, "Texture manager unable to load bundled file: %s", strTextureName.c_str()); + return 0; + } + + pMap = new CTextureMap(strTextureName, width, height, nLoops); + for (int iImage = 0; iImage < nImages; ++iImage) + { + pMap->Add(pTextures[iImage], Delay[iImage]); + } + + delete [] pTextures; + delete [] Delay; + } + else + { + CAnimatedGifSet AnimatedGifSet; + int iImages = AnimatedGifSet.LoadGIF(strPath.c_str()); + if (iImages == 0) + { + if (!strnicmp(strPath.c_str(), "special://home/skin/", 20) && !strnicmp(strPath.c_str(), "special://xbmc/skin/", 20)) + CLog::Log(LOGERROR, "Texture manager unable to load file: %s", strPath.c_str()); + return 0; + } + int iWidth = AnimatedGifSet.FrameWidth; + int iHeight = AnimatedGifSet.FrameHeight; + + // fixup our palette + COLOR *palette = AnimatedGifSet.m_vecimg[0]->Palette; + // set the alpha values to fully opaque + for (int i = 0; i < 256; i++) + palette[i].x = 0xff; + // and set the transparent colour + if (AnimatedGifSet.m_vecimg[0]->Transparency && AnimatedGifSet.m_vecimg[0]->Transparent >= 0) + palette[AnimatedGifSet.m_vecimg[0]->Transparent].x = 0; + + pMap = new CTextureMap(strTextureName, iWidth, iHeight, AnimatedGifSet.nLoops); + + for (int iImage = 0; iImage < iImages; iImage++) + { + CTexture *glTexture = new CTexture(); + if (glTexture) + { + CAnimatedGif* pImage = AnimatedGifSet.m_vecimg[iImage]; + glTexture->LoadPaletted(pImage->Width, pImage->Height, pImage->BytesPerRow, (unsigned char *)pImage->Raster, palette); + pMap->Add(glTexture, pImage->Delay); + } + } // of for (int iImage=0; iImage < iImages; iImage++) + } + +#ifdef _DEBUG + LARGE_INTEGER end, freq; + QueryPerformanceCounter(&end); + QueryPerformanceFrequency(&freq); + char temp[200]; + sprintf(temp, "Load %s: %.1fms%s\n", strPath.c_str(), 1000.f * (end.QuadPart - start.QuadPart) / freq.QuadPart, (bundle >= 0) ? " (bundled)" : ""); + OutputDebugString(temp); +#endif + + m_vecTextures.push_back(pMap); + return 1; + } // of if (strPath.Right(4).ToLower()==".gif") + + CBaseTexture *pTexture = NULL; + int width = 0, height = 0; + if (bundle >= 0) + { + if (FAILED(m_TexBundle[bundle].LoadTexture(strTextureName, &pTexture, width, height))) + { + CLog::Log(LOGERROR, "Texture manager unable to load bundled file: %s", strTextureName.c_str()); + return 0; + } + } + else + { + // normal picture + // convert from utf8 + CStdString texturePath; + g_charsetConverter.utf8ToStringCharset(strPath, texturePath); + + pTexture = new CTexture(); + pTexture->LoadFromFile(texturePath); + width = pTexture->GetWidth(); + height = pTexture->GetHeight(); + } + + if (!pTexture) return 0; + + CTextureMap* pMap = new CTextureMap(strTextureName, width, height, 0); + pMap->Add(pTexture, 100); + m_vecTextures.push_back(pMap); + +#ifdef _DEBUG_TEXTURES + LARGE_INTEGER end, freq; + QueryPerformanceCounter(&end); + QueryPerformanceFrequency(&freq); + char temp[200]; + sprintf(temp, "Load %s: %.1fms%s\n", strPath.c_str(), 1000.f * (end.QuadPart - start.QuadPart) / freq.QuadPart, (bundle >= 0) ? " (bundled)" : ""); + OutputDebugString(temp); +#endif + + return 1; +} + + +void CGUITextureManager::ReleaseTexture(const CStdString& strTextureName) +{ + CSingleLock lock(g_graphicsContext); + + ivecTextures i; + i = m_vecTextures.begin(); + while (i != m_vecTextures.end()) + { + CTextureMap* pMap = *i; + if (pMap->GetName() == strTextureName) + { + if (pMap->Release()) + { + //CLog::Log(LOGINFO, " cleanup:%s", strTextureName.c_str()); + // add to our textures to free + m_unusedTextures.push_back(pMap); + i = m_vecTextures.erase(i); + } + return; + } + ++i; + } + CLog::Log(LOGWARNING, "%s: Unable to release texture %s", __FUNCTION__, strTextureName.c_str()); +} + +void CGUITextureManager::FreeUnusedTextures() +{ + CSingleLock lock(g_graphicsContext); + for (ivecTextures i = m_unusedTextures.begin(); i != m_unusedTextures.end(); ++i) + delete *i; + m_unusedTextures.clear(); +} + +void CGUITextureManager::Cleanup() +{ + CSingleLock lock(g_graphicsContext); + + ivecTextures i; + i = m_vecTextures.begin(); + while (i != m_vecTextures.end()) + { + CTextureMap* pMap = *i; + CLog::Log(LOGWARNING, "%s: Having to cleanup texture %s", __FUNCTION__, pMap->GetName().c_str()); + delete pMap; + i = m_vecTextures.erase(i); + } + for (int i = 0; i < 2; i++) + m_TexBundle[i].Cleanup(); +} + +void CGUITextureManager::Dump() const +{ + CStdString strLog; + strLog.Format("total texturemaps size:%i\n", m_vecTextures.size()); + OutputDebugString(strLog.c_str()); + + for (int i = 0; i < (int)m_vecTextures.size(); ++i) + { + const CTextureMap* pMap = m_vecTextures[i]; + if (!pMap->IsEmpty()) + pMap->Dump(); + } +} + +void CGUITextureManager::Flush() +{ + CSingleLock lock(g_graphicsContext); + + ivecTextures i; + i = m_vecTextures.begin(); + while (i != m_vecTextures.end()) + { + CTextureMap* pMap = *i; + pMap->Flush(); + if (pMap->IsEmpty() ) + { + delete pMap; + i = m_vecTextures.erase(i); + } + else + { + ++i; + } + } +} + +DWORD CGUITextureManager::GetMemoryUsage() const +{ + DWORD memUsage = 0; + for (int i = 0; i < (int)m_vecTextures.size(); ++i) + { + memUsage += m_vecTextures[i]->GetMemoryUsage(); + } + return memUsage; +} + +void CGUITextureManager::SetTexturePath(const CStdString &texturePath) +{ + m_texturePaths.clear(); + AddTexturePath(texturePath); +} + +void CGUITextureManager::AddTexturePath(const CStdString &texturePath) +{ + if (!texturePath.IsEmpty()) + m_texturePaths.push_back(texturePath); +} + +void CGUITextureManager::RemoveTexturePath(const CStdString &texturePath) +{ + for (vector<CStdString>::iterator it = m_texturePaths.begin(); it != m_texturePaths.end(); ++it) + { + if (*it == texturePath) + { + m_texturePaths.erase(it); + return; + } + } +} + +CStdString CGUITextureManager::GetTexturePath(const CStdString &textureName, bool directory /* = false */) +{ + if (CURL::IsFullPath(textureName)) + return textureName; + else + { // texture doesn't include the full path, so check all fallbacks + for (vector<CStdString>::iterator it = m_texturePaths.begin(); it != m_texturePaths.end(); ++it) + { + CStdString path = CUtil::AddFileToFolder(it->c_str(), "media"); + path = CUtil::AddFileToFolder(path, textureName); + if (directory) + { + if (DIRECTORY::CDirectory::Exists(path)) + return path; + } + else + { + if (XFILE::CFile::Exists(path)) + return path; + } + } + } + return ""; +} + +void CGUITextureManager::GetBundledTexturesFromPath(const CStdString& texturePath, std::vector<CStdString> &items) +{ + m_TexBundle[0].GetTexturesFromPath(texturePath, items); + if (items.empty()) + m_TexBundle[1].GetTexturesFromPath(texturePath, items); +} diff --git a/guilib/TextureManager.h b/guilib/TextureManager.h new file mode 100644 index 0000000000..8907714011 --- /dev/null +++ b/guilib/TextureManager.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +/*! +\file TextureManager.h +\brief +*/ + +#ifndef GUILIB_TEXTUREMANAGER_H +#define GUILIB_TEXTUREMANAGER_H + +#include <vector> +#include "TextureBundle.h" + +#pragma once + +/************************************************************************/ +/* */ +/************************************************************************/ +class CTextureArray +{ +public: + CTextureArray(int width, int height, int loops, bool texCoordsArePixels = false); + CTextureArray(); + + virtual ~CTextureArray(); + + void Reset(); + + void Add(CBaseTexture *texture, int delay); + void Set(CBaseTexture *texture, int width, int height); + void Free(); + unsigned int size() const; + + std::vector<CBaseTexture* > m_textures; + std::vector<int> m_delays; + int m_width; + int m_height; + int m_loops; + int m_texWidth; + int m_texHeight; + bool m_texCoordsArePixels; +}; + +/*! + \ingroup textures + \brief + */ +/************************************************************************/ +/* */ +/************************************************************************/ +class CTextureMap +{ +public: + CTextureMap(); + CTextureMap(const CStdString& textureName, int width, int height, int loops); + virtual ~CTextureMap(); + + void Add(CBaseTexture* texture, int delay); + bool Release(); + + const CStdString& GetName() const; + const CTextureArray& GetTexture(); + void Dump() const; + DWORD GetMemoryUsage() const; + void Flush(); + bool IsEmpty() const; +protected: + void FreeTexture(); + + CStdString m_textureName; + CTextureArray m_texture; + unsigned int m_referenceCount; + DWORD m_memUsage; +}; + +/*! + \ingroup textures + \brief + */ +/************************************************************************/ +/* */ +/************************************************************************/ +class CGUITextureManager +{ +public: + CGUITextureManager(void); + virtual ~CGUITextureManager(void); + + bool HasTexture(const CStdString &textureName, CStdString *path = NULL, int *bundle = NULL, int *size = NULL); + bool CanLoad(const CStdString &texturePath) const; ///< Returns true if the texture manager can load this texture + int Load(const CStdString& strTextureName, bool checkBundleOnly = false); + const CTextureArray& GetTexture(const CStdString& strTextureName); + void ReleaseTexture(const CStdString& strTextureName); + void Cleanup(); + void Dump() const; + DWORD GetMemoryUsage() const; + void Flush(); + CStdString GetTexturePath(const CStdString& textureName, bool directory = false); + void GetBundledTexturesFromPath(const CStdString& texturePath, std::vector<CStdString> &items); + + void AddTexturePath(const CStdString &texturePath); ///< Add a new path to the paths to check when loading media + void SetTexturePath(const CStdString &texturePath); ///< Set a single path as the path to check when loading media (clear then add) + void RemoveTexturePath(const CStdString &texturePath); ///< Remove a path from the paths to check when loading media + + void FreeUnusedTextures(); ///< Free textures (called from app thread only) +protected: + std::vector<CTextureMap*> m_vecTextures; + std::vector<CTextureMap*> m_unusedTextures; + typedef std::vector<CTextureMap*>::iterator ivecTextures; + // we have 2 texture bundles (one for the base textures, one for the theme) + CTextureBundle m_TexBundle[2]; + + std::vector<CStdString> m_texturePaths; +}; + +/*! + \ingroup textures + \brief + */ +extern CGUITextureManager g_TextureManager; +#endif diff --git a/guilib/TransformMatrix.h b/guilib/TransformMatrix.h new file mode 100644 index 0000000000..cc9c19bae6 --- /dev/null +++ b/guilib/TransformMatrix.h @@ -0,0 +1,240 @@ +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include <math.h> +#include <memory> +#include <string.h> +#include <stdint.h> + +#ifdef __GNUC__ +// under gcc, inline will only take place if optimizations are applied (-O). this will force inline even whith optimizations. +#define XBMC_FORCE_INLINE __attribute__((always_inline)) +#else +#define XBMC_FORCE_INLINE +#endif + +typedef uint32_t color_t; + +class TransformMatrix +{ +public: + TransformMatrix() + { + Reset(); + }; + void Reset() + { + m[0][0] = 1.0f; m[0][1] = m[0][2] = m[0][3] = 0.0f; + m[1][0] = m[1][2] = m[1][3] = 0.0f; m[1][1] = 1.0f; + m[2][0] = m[2][1] = m[2][3] = 0.0f; m[2][2] = 1.0f; + alpha = 1.0f; + }; + static TransformMatrix CreateTranslation(float transX, float transY, float transZ = 0) + { + TransformMatrix translation; + translation.m[0][3] = transX; + translation.m[1][3] = transY; + translation.m[2][3] = transZ; + return translation; + } + void SetTranslation(float transX, float transY, float transZ) + { + m[0][1] = m[0][2] = 0.0f; m[0][0] = 1.0f; m[0][3] = transX; + m[1][0] = m[1][2] = 0.0f; m[1][1] = 1.0f; m[1][3] = transY; + m[2][0] = m[2][1] = 0.0f; m[2][2] = 1.0f; m[2][3] = transZ; + alpha = 1.0f; + } + static TransformMatrix CreateScaler(float scaleX, float scaleY, float scaleZ = 1.0f) + { + TransformMatrix scaler; + scaler.m[0][0] = scaleX; + scaler.m[1][1] = scaleY; + scaler.m[2][2] = scaleZ; + return scaler; + }; + void SetScaler(float scaleX, float scaleY, float centerX, float centerY) + { + // Trans(centerX,centerY,centerZ)*Scale(scaleX,scaleY,scaleZ)*Trans(-centerX,-centerY,-centerZ) + float centerZ = 0.0f, scaleZ = 1.0f; + m[0][0] = scaleX; m[0][1] = 0.0f; m[0][2] = 0.0f; m[0][3] = centerX*(1-scaleX); + m[1][0] = 0.0f; m[1][1] = scaleY; m[1][2] = 0.0f; m[1][3] = centerY*(1-scaleY); + m[2][0] = 0.0f; m[2][1] = 0.0f; m[2][2] = scaleZ; m[2][3] = centerZ*(1-scaleZ); + alpha = 1.0f; + }; + void SetXRotation(float angle, float y, float z, float ar = 1.0f) + { // angle about the X axis, centered at y,z where our coordinate system has aspect ratio ar. + // Trans(0,y,z)*Scale(1,1/ar,1)*RotateX(angle)*Scale(ar,1,1)*Trans(0,-y,-z); + float c = cos(angle); float s = sin(angle); + m[0][0] = ar; m[0][1] = 0.0f; m[0][2] = 0.0f; m[0][3] = 0.0f; + m[1][0] = 0.0f; m[1][1] = c/ar; m[1][2] = -s/ar; m[1][3] = (-y*c+s*z)/ar + y; + m[2][0] = 0.0f; m[2][1] = s; m[2][2] = c; m[2][3] = (-y*s-c*z) + z; + angle = 1.0f; + } + void SetYRotation(float angle, float x, float z, float ar = 1.0f) + { // angle about the Y axis, centered at x,z where our coordinate system has aspect ratio ar. + // Trans(x,0,z)*Scale(1/ar,1,1)*RotateY(angle)*Scale(ar,1,1)*Trans(-x,0,-z); + float c = cos(angle); float s = sin(angle); + m[0][0] = c; m[0][1] = 0.0f; m[0][2] = -s/ar; m[0][3] = -x*c + s*z/ar + x; + m[1][0] = 0.0f; m[1][1] = 1.0f; m[1][2] = 0.0f; m[1][3] = 0.0f; + m[2][0] = ar*s; m[2][1] = 0.0f; m[2][2] = c; m[2][3] = -ar*x*s - c*z + z; + angle = 1.0f; + } + static TransformMatrix CreateZRotation(float angle, float x, float y, float ar = 1.0f) + { // angle about the Z axis, centered at x,y where our coordinate system has aspect ratio ar. + // Trans(x,y,0)*Scale(1/ar,1,1)*RotateZ(angle)*Scale(ar,1,1)*Trans(-x,-y,0) + float c = cos(angle); float s = sin(angle); + TransformMatrix rot; + rot.m[0][0] = c; rot.m[0][1] = -s/ar; rot.m[0][3] = -x*c + s*y/ar + x; + rot.m[1][0] = s*ar; rot.m[1][1] = c; rot.m[1][3] = -ar*x*s - c*y + y; + return rot; + } + void SetZRotation(float angle, float x, float y, float ar = 1.0f) + { // angle about the Z axis, centered at x,y where our coordinate system has aspect ratio ar. + // Trans(x,y,0)*Scale(1/ar,1,1)*RotateZ(angle)*Scale(ar,1,1)*Trans(-x,-y,0) + float c = cos(angle); float s = sin(angle); + m[0][0] = c; m[0][1] = -s/ar; m[0][2] = 0.0f; m[0][3] = -x*c + s*y/ar + x; + m[1][0] = s*ar; m[1][1] = c; m[1][2] = 0.0f; m[1][3] = -ar*x*s - c*y + y; + m[2][0] = 0.0f; m[2][1] = 0.0f; m[2][2] = 1.0f; m[2][3] = 0.0f; + angle = 1.0f; + } + static TransformMatrix CreateFader(float a) + { + TransformMatrix fader; + fader.alpha = a; + return fader; + } + void SetFader(float a) + { + m[0][0] = 1.0f; m[0][1] = 0.0f; m[0][2] = 0.0f; m[0][3] = 0.0f; + m[1][0] = 0.0f; m[1][1] = 1.0f; m[1][2] = 0.0f; m[1][3] = 0.0f; + m[2][0] = 0.0f; m[2][1] = 0.0f; m[2][2] = 1.0f; m[2][3] = 0.0f; + alpha = a; + } + // assignment operator + const TransformMatrix &operator =(const TransformMatrix &right) + { + if (this != &right) + { + memcpy(m, right.m, 12*sizeof(float)); + alpha = right.alpha; + } + return *this; + } + + // multiplication operators + const TransformMatrix &operator *=(const TransformMatrix &right) + { + float t00 = m[0][0] * right.m[0][0] + m[0][1] * right.m[1][0] + m[0][2] * right.m[2][0]; + float t01 = m[0][0] * right.m[0][1] + m[0][1] * right.m[1][1] + m[0][2] * right.m[2][1]; + float t02 = m[0][0] * right.m[0][2] + m[0][1] * right.m[1][2] + m[0][2] * right.m[2][2]; + m[0][3] = m[0][0] * right.m[0][3] + m[0][1] * right.m[1][3] + m[0][2] * right.m[2][3] + m[0][3]; + m[0][0] = t00; m[0][1] = t01; m[0][2] = t02; + t00 = m[1][0] * right.m[0][0] + m[1][1] * right.m[1][0] + m[1][2] * right.m[2][0]; + t01 = m[1][0] * right.m[0][1] + m[1][1] * right.m[1][1] + m[1][2] * right.m[2][1]; + t02 = m[1][0] * right.m[0][2] + m[1][1] * right.m[1][2] + m[1][2] * right.m[2][2]; + m[1][3] = m[1][0] * right.m[0][3] + m[1][1] * right.m[1][3] + m[1][2] * right.m[2][3] + m[1][3]; + m[1][0] = t00; m[1][1] = t01; m[1][2] = t02; + t00 = m[2][0] * right.m[0][0] + m[2][1] * right.m[1][0] + m[2][2] * right.m[2][0]; + t01 = m[2][0] * right.m[0][1] + m[2][1] * right.m[1][1] + m[2][2] * right.m[2][1]; + t02 = m[2][0] * right.m[0][2] + m[2][1] * right.m[1][2] + m[2][2] * right.m[2][2]; + m[2][3] = m[2][0] * right.m[0][3] + m[2][1] * right.m[1][3] + m[2][2] * right.m[2][3] + m[2][3]; + m[2][0] = t00; m[2][1] = t01; m[2][2] = t02; + alpha *= right.alpha; + return *this; + } + + TransformMatrix operator *(const TransformMatrix &right) const + { + TransformMatrix result; + result.m[0][0] = m[0][0] * right.m[0][0] + m[0][1] * right.m[1][0] + m[0][2] * right.m[2][0]; + result.m[0][1] = m[0][0] * right.m[0][1] + m[0][1] * right.m[1][1] + m[0][2] * right.m[2][1]; + result.m[0][2] = m[0][0] * right.m[0][2] + m[0][1] * right.m[1][2] + m[0][2] * right.m[2][2]; + result.m[0][3] = m[0][0] * right.m[0][3] + m[0][1] * right.m[1][3] + m[0][2] * right.m[2][3] + m[0][3]; + result.m[1][0] = m[1][0] * right.m[0][0] + m[1][1] * right.m[1][0] + m[1][2] * right.m[2][0]; + result.m[1][1] = m[1][0] * right.m[0][1] + m[1][1] * right.m[1][1] + m[1][2] * right.m[2][1]; + result.m[1][2] = m[1][0] * right.m[0][2] + m[1][1] * right.m[1][2] + m[1][2] * right.m[2][2]; + result.m[1][3] = m[1][0] * right.m[0][3] + m[1][1] * right.m[1][3] + m[1][2] * right.m[2][3] + m[1][3]; + result.m[2][0] = m[2][0] * right.m[0][0] + m[2][1] * right.m[1][0] + m[2][2] * right.m[2][0]; + result.m[2][1] = m[2][0] * right.m[0][1] + m[2][1] * right.m[1][1] + m[2][2] * right.m[2][1]; + result.m[2][2] = m[2][0] * right.m[0][2] + m[2][1] * right.m[1][2] + m[2][2] * right.m[2][2]; + result.m[2][3] = m[2][0] * right.m[0][3] + m[2][1] * right.m[1][3] + m[2][2] * right.m[2][3] + m[2][3]; + result.alpha = alpha * right.alpha; + return result; + } + + inline void TransformPosition(float &x, float &y, float &z) const XBMC_FORCE_INLINE + { + float newX = m[0][0] * x + m[0][1] * y + m[0][2] * z + m[0][3]; + float newY = m[1][0] * x + m[1][1] * y + m[1][2] * z + m[1][3]; + z = m[2][0] * x + m[2][1] * y + m[2][2] * z + m[2][3]; + y = newY; + x = newX; + } + + inline void TransformPositionUnscaled(float &x, float &y, float &z) const XBMC_FORCE_INLINE + { + float n; + // calculate the norm of the transformed (but not translated) vectors involved + n = sqrt(m[0][0]*m[0][0] + m[0][1]*m[0][1] + m[0][2]*m[0][2]); + float newX = (m[0][0] * x + m[0][1] * y + m[0][2] * z)/n + m[0][3]; + n = sqrt(m[1][0]*m[1][0] + m[1][1]*m[1][1] + m[1][2]*m[1][2]); + float newY = (m[1][0] * x + m[1][1] * y + m[1][2] * z)/n + m[1][3]; + n = sqrt(m[2][0]*m[2][0] + m[2][1]*m[2][1] + m[2][2]*m[2][2]); + float newZ = (m[2][0] * x + m[2][1] * y + m[2][2] * z)/n + m[2][3]; + z = newZ; + y = newY; + x = newX; + } + + inline void InverseTransformPosition(float &x, float &y) const XBMC_FORCE_INLINE + { // used for mouse - no way to find z + x -= m[0][3]; y -= m[1][3]; + float detM = m[0][0]*m[1][1] - m[0][1]*m[1][0]; + float newX = (m[1][1] * x - m[0][1] * y)/detM; + y = (-m[1][0] * x + m[0][0] * y)/detM; + x = newX; + } + + inline float TransformXCoord(float x, float y, float z) const XBMC_FORCE_INLINE + { + return m[0][0] * x + m[0][1] * y + m[0][2] * z + m[0][3]; + } + + inline float TransformYCoord(float x, float y, float z) const XBMC_FORCE_INLINE + { + return m[1][0] * x + m[1][1] * y + m[1][2] * z + m[1][3]; + } + + inline float TransformZCoord(float x, float y, float z) const XBMC_FORCE_INLINE + { + return m[2][0] * x + m[2][1] * y + m[2][2] * z + m[2][3]; + } + + inline color_t TransformAlpha(color_t colour) const XBMC_FORCE_INLINE + { + return (color_t)(colour * alpha); + } + + float m[3][4]; + float alpha; +}; diff --git a/guilib/Tween.h b/guilib/Tween.h new file mode 100644 index 0000000000..9df3bdcbb7 --- /dev/null +++ b/guilib/Tween.h @@ -0,0 +1,408 @@ +#ifndef __TWEEN_H__ +#define __TWEEN_H__ + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +/////////////////////////////////////////////////////////////////////// +// Tween.h +// A couple of tweening classes implemented in C++. +// ref: http://www.robertpenner.com/easing/ +// +// Author: d4rk <d4rk@xbmc.org> +/////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////// +// Current list of classes: +// +// LinearTweener +// QuadTweener +// CubicTweener +// SineTweener +// CircleTweener +// BackTweener +// BounceTweener +// ElasticTweener +// +/////////////////////////////////////////////////////////////////////// + +#include <math.h> + +#ifndef M_PI +#define M_PI 3.14159265358979323846f +#endif + +enum TweenerType +{ + EASE_IN, + EASE_OUT, + EASE_INOUT +}; + + +class Tweener +{ +public: + Tweener(TweenerType tweenerType = EASE_OUT) { m_tweenerType = tweenerType; _ref=1; } + virtual ~Tweener() {}; + + void SetEasing(TweenerType type) { m_tweenerType = type; } + virtual float Tween(float time, float start, float change, float duration)=0; + void Free() { _ref--; if (_ref==0) delete this; } + void IncRef() { _ref++; } + +protected: + int _ref; + TweenerType m_tweenerType; +}; + + +class LinearTweener : public Tweener +{ +public: + virtual float Tween(float time, float start, float change, float duration) + { + return change * time / duration + start; + } +}; + + +class QuadTweener : public Tweener +{ +public: + QuadTweener(float a = 1.0f) { _a=a; } + virtual float Tween(float time, float start, float change, float duration) + { + switch (m_tweenerType) + { + case EASE_IN: + time /= duration; + return change * time * (_a * time + 1 - _a) + start; + break; + + case EASE_OUT: + time /= duration; + return -change * time * (_a * time - 1 - _a) + start; + break; + + case EASE_INOUT: + time /= duration/2; + if (time < 1) + return (change) * time * (_a * time + 1 - _a) + start; + time--; + return (-change) * time * (_a * time - 1 - _a) + start; + break; + } + return change * time * time + start; + } +private: + float _a; +}; + + +class CubicTweener : public Tweener +{ +public: + virtual float Tween(float time, float start, float change, float duration) + { + switch (m_tweenerType) + { + case EASE_IN: + time /= duration; + return change * time * time * time + start; + break; + + case EASE_OUT: + time /= duration; + time--; + return change * (time * time * time + 1) + start; + break; + + case EASE_INOUT: + time /= duration/2; + if (time < 1) + return (change/2) * time * time * time + start; + time-=2; + return (change/2) * (time * time * time + 2) + start; + break; + } + return change * time * time + start; + } +}; + +class CircleTweener : public Tweener +{ +public: + virtual float Tween(float time, float start, float change, float duration) + { + switch (m_tweenerType) + { + case EASE_IN: + time /= duration; + return (-change) * (sqrt(1 - time * time) - 1) + start; + break; + + case EASE_OUT: + time /= duration; + time--; + return change * sqrt(1 - time * time) + start; + break; + + case EASE_INOUT: + time /= duration/2; + if (time < 1) + return (-change/2) * (sqrt(1 - time * time) - 1) + start; + time-=2; + return change/2 * (sqrt(1 - time * time) + 1) + start; + break; + } + return change * sqrt(1 - time * time) + start; + } +}; + +class BackTweener : public Tweener +{ +public: + BackTweener(float s=1.70158) { _s=s; } + + virtual float Tween(float time, float start, float change, float duration) + { + float s = _s; + switch (m_tweenerType) + { + case EASE_IN: + time /= duration; + return change * time * time * ((s + 1) * time - s) + start; + break; + + case EASE_OUT: + time /= duration; + time--; + return change * (time * time * ((s + 1) * time + s) + 1) + start; + break; + + case EASE_INOUT: + time /= duration/2; + s*=(1.525f); + if ((time ) < 1) + { + return (change/2) * (time * time * ((s + 1) * time - s)) + start; + } + time-=2; + return (change/2) * (time * time * ((s + 1) * time + s) + 2) + start; + break; + } + return change * ((time-1) * time * ((s + 1) * time + s) + 1) + start; + } + +private: + float _s; + +}; + + +class SineTweener : public Tweener +{ +public: + virtual float Tween(float time, float start, float change, float duration) + { + time /= duration; + switch (m_tweenerType) + { + case EASE_IN: + return change * (1 - cos(time * M_PI / 2.0f)) + start; + break; + + case EASE_OUT: + return change * sin(time * M_PI / 2.0f) + start; + break; + + case EASE_INOUT: + return change/2 * (1 - cos(M_PI * time)) + start; + break; + } + return (change/2) * (1 - cos(M_PI * time)) + start; + } +}; + + +class BounceTweener : public Tweener +{ +public: + virtual float Tween(float time, float start, float change, float duration) + { + switch (m_tweenerType) + { + case EASE_IN: + return (change - easeOut(duration - time, 0, change, duration)) + start; + break; + + case EASE_OUT: + return easeOut(time, start, change, duration); + break; + + case EASE_INOUT: + if (time < duration/2) + return (change - easeOut (duration - (time * 2), 0, change, duration) + start) * .5f + start; + else + return (easeOut (time * 2 - duration, 0, change, duration) * .5f + change * .5f) + start; + break; + } + + return easeOut(time, start, change, duration); + } + +protected: + float easeOut(float time, float start, float change, float duration) + { + time /= duration; + if (time < (1/2.75)) { + return change * (7.5625f * time * time) + start; + } else if (time < (2/2.75)) { + time -= (1.5f/2.75f); + return change * (7.5625f * time * time + .75f) + start; + } else if (time < (2.5/2.75)) { + time -= (2.25f/2.75f); + return change * (7.5625f * time * time + .9375f) + start; + } else { + time -= (2.625f/2.75f); + return change * (7.5625f * time * time + .984375f) + start; + } + } +}; + + +class ElasticTweener : public Tweener +{ +public: + ElasticTweener(float a=0.0, float p=0.0) { _a=a; _p=p; } + + virtual float Tween(float time, float start, float change, float duration) + { + switch (m_tweenerType) + { + case EASE_IN: + return easeIn(time, start, change, duration); + break; + + case EASE_OUT: + return easeOut(time, start, change, duration); + break; + + case EASE_INOUT: + return easeInOut(time, start, change, duration); + break; + } + return easeOut(time, start, change, duration); + } + +protected: + float _a; + float _p; + + float easeIn(float time, float start, float change, float duration) + { + float s=0; + float a=_a; + float p=_p; + + if (time==0) + return start; + time /= duration; + if (time==1) + return start + change; + if (!p) + p=duration*.3f; + if (!a || a < fabs(change)) + { + a = change; + s = p / 4.0f; + } + else + { + s = p / (2 * M_PI) * asin (change / a); + } + time--; + return -(a * pow(2.0f, 10*time) * sin((time * duration - s) * (2 * M_PI) / p )) + start; + } + + float easeOut(float time, float start, float change, float duration) + { + float s=0; + float a=_a; + float p=_p; + + if (time==0) + return start; + time /= duration; + if (time==1) + return start + change; + if (!p) + p=duration*.3f; + if (!a || a < fabs(change)) + { + a = change; + s = p / 4.0f; + } + else + { + s = p / (2 * M_PI) * asin (change / a); + } + return (a * pow(2.0f, -10*time) * sin((time * duration - s) * (2 * M_PI) / p )) + change + start; + } + + float easeInOut(float time, float start, float change, float duration) + { + float s=0; + float a=_a; + float p=_p; + + if (time==0) + return start; + time /= duration/2; + if (time==2) + return start + change; + if (!p) + p=duration*.3f*1.5f; + if (!a || a < fabs(change)) + { + a = change; + s = p / 4.0f; + } + else + { + s = p / (2 * M_PI) * asin (change / a); + } + + if (time < 1) + { + time--; + return -.5f * (a * pow(2.0f, 10 * (time)) * sin((time * duration - s) * (2 * M_PI) / p )) + start; + } + time--; + return a * pow(2.0f, -10 * (time)) * sin((time * duration-s) * (2 * M_PI) / p ) * .5f + change + start; + } +}; + + + +#endif // __TWEEN_H__ diff --git a/guilib/VisibleEffect.cpp b/guilib/VisibleEffect.cpp new file mode 100644 index 0000000000..df48066142 --- /dev/null +++ b/guilib/VisibleEffect.cpp @@ -0,0 +1,703 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "VisibleEffect.h" +#include "utils/GUIInfoManager.h" +#include "utils/log.h" +#include "SkinInfo.h" // for the effect time adjustments +#include "StringUtils.h" +#include "GUIImage.h" // for FRECT +#include "Tween.h" +#include "tinyXML/tinyxml.h" + +using namespace std; + +CAnimEffect::CAnimEffect(const TiXmlElement *node, EFFECT_TYPE effect) +{ + m_effect = effect; + // defaults + m_delay = m_length = 0; + m_pTweener = NULL; + // time and delay + + float temp; + if (g_SkinInfo.ResolveConstant(node->Attribute("time"), temp)) m_length = (unsigned int)(temp * g_SkinInfo.GetEffectsSlowdown()); + if (g_SkinInfo.ResolveConstant(node->Attribute("delay"), temp)) m_delay = (unsigned int)(temp * g_SkinInfo.GetEffectsSlowdown()); + + const char *tween = node->Attribute("tween"); + if (tween) + { + if (strcmpi(tween, "linear")==0) + m_pTweener = new LinearTweener(); + else if (strcmpi(tween, "quadratic")==0) + m_pTweener = new QuadTweener(); + else if (strcmpi(tween, "cubic")==0) + m_pTweener = new CubicTweener(); + else if (strcmpi(tween, "sine")==0) + m_pTweener = new SineTweener(); + else if (strcmpi(tween, "back")==0) + m_pTweener = new BackTweener(); + else if (strcmpi(tween, "circle")==0) + m_pTweener = new CircleTweener(); + else if (strcmpi(tween, "bounce")==0) + m_pTweener = new BounceTweener(); + else if (strcmpi(tween, "elastic")==0) + m_pTweener = new ElasticTweener(); + + const char *easing = node->Attribute("easing"); + if (m_pTweener && easing) + { + if (strcmpi(easing, "in")==0) + m_pTweener->SetEasing(EASE_IN); + else if (strcmpi(easing, "out")==0) + m_pTweener->SetEasing(EASE_OUT); + else if (strcmpi(easing, "inout")==0) + m_pTweener->SetEasing(EASE_INOUT); + } + } + + float accel; + g_SkinInfo.ResolveConstant(node->Attribute("acceleration"), accel); + + if (!m_pTweener) + { // no tweener is specified - use a linear tweener + // or quadratic if we have acceleration + if (accel) + { + m_pTweener = new QuadTweener(accel); + m_pTweener->SetEasing(EASE_IN); + } + else + m_pTweener = new LinearTweener(); + } +} + +CAnimEffect::CAnimEffect(unsigned int delay, unsigned int length, EFFECT_TYPE effect) +{ + m_delay = delay; + m_length = length; + m_effect = effect; + m_pTweener = new LinearTweener(); +} + +CAnimEffect::~CAnimEffect() +{ + if (m_pTweener) + m_pTweener->Free(); +} + +CAnimEffect::CAnimEffect(const CAnimEffect &src) +{ + m_pTweener = NULL; + *this = src; +} + +const CAnimEffect &CAnimEffect::operator=(const CAnimEffect &src) +{ + if (&src == this) return *this; + + m_matrix = src.m_matrix; + m_effect = src.m_effect; + m_length = src.m_length; + m_delay = src.m_delay; + + if (m_pTweener) + m_pTweener->Free(); + m_pTweener = src.m_pTweener; + if (m_pTweener) + m_pTweener->IncRef(); + return *this; +} + +void CAnimEffect::Calculate(unsigned int time, const CPoint ¢er) +{ + assert(m_delay + m_length); + // calculate offset and tweening + float offset = 0.0f; // delayed forward, or finished reverse + if (time >= m_delay && time < m_delay + m_length) + offset = (float)(time - m_delay) / m_length; + else if (time >= m_delay + m_length) + offset = 1.0f; + if (m_pTweener) + offset = m_pTweener->Tween(offset, 0.0f, 1.0f, 1.0f); + // and apply the effect + ApplyEffect(offset, center); +} + +void CAnimEffect::ApplyState(ANIMATION_STATE state, const CPoint ¢er) +{ + float offset = (state == ANIM_STATE_APPLIED) ? 1.0f : 0.0f; + ApplyEffect(offset, center); +} + +CFadeEffect::CFadeEffect(const TiXmlElement *node, bool reverseDefaults) : CAnimEffect(node, EFFECT_TYPE_FADE) +{ + if (reverseDefaults) + { // out effect defaults + m_startAlpha = 100.0f; + m_endAlpha = 0; + } + else + { // in effect defaults + m_startAlpha = 0; + m_endAlpha = 100.0f; + } + if (node->Attribute("start")) g_SkinInfo.ResolveConstant(node->Attribute("start"), m_startAlpha); + if (node->Attribute("end")) g_SkinInfo.ResolveConstant(node->Attribute("end"), m_endAlpha); + if (m_startAlpha > 100.0f) m_startAlpha = 100.0f; + if (m_endAlpha > 100.0f) m_endAlpha = 100.0f; + if (m_startAlpha < 0) m_startAlpha = 0; + if (m_endAlpha < 0) m_endAlpha = 0; +} + +CFadeEffect::CFadeEffect(float start, float end, unsigned int delay, unsigned int length) : CAnimEffect(delay, length, EFFECT_TYPE_FADE) +{ + m_startAlpha = start; + m_endAlpha = end; +} + +void CFadeEffect::ApplyEffect(float offset, const CPoint ¢er) +{ + m_matrix.SetFader(((m_endAlpha - m_startAlpha) * offset + m_startAlpha) * 0.01f); +} + +CSlideEffect::CSlideEffect(const TiXmlElement *node) : CAnimEffect(node, EFFECT_TYPE_SLIDE) +{ + m_startX = m_endX = 0; + m_startY = m_endY = 0; + const char *startPos = node->Attribute("start"); + if (startPos) + { + vector<CStdString> commaSeparated; + StringUtils::SplitString(startPos, ",", commaSeparated); + if (commaSeparated.size() > 1) + g_SkinInfo.ResolveConstant(commaSeparated[1], m_startY); + g_SkinInfo.ResolveConstant(commaSeparated[0], m_startX); + } + const char *endPos = node->Attribute("end"); + if (endPos) + { + vector<CStdString> commaSeparated; + StringUtils::SplitString(endPos, ",", commaSeparated); + if (commaSeparated.size() > 1) + g_SkinInfo.ResolveConstant(commaSeparated[1], m_endY); + g_SkinInfo.ResolveConstant(commaSeparated[0], m_endX); + } +} + +void CSlideEffect::ApplyEffect(float offset, const CPoint ¢er) +{ + m_matrix.SetTranslation((m_endX - m_startX)*offset + m_startX, (m_endY - m_startY)*offset + m_startY, 0); +} + +CRotateEffect::CRotateEffect(const TiXmlElement *node, EFFECT_TYPE effect) : CAnimEffect(node, effect) +{ + m_startAngle = m_endAngle = 0; + m_autoCenter = false; + if (node->Attribute("start")) g_SkinInfo.ResolveConstant(node->Attribute("start"), m_startAngle); + if (node->Attribute("end")) g_SkinInfo.ResolveConstant(node->Attribute("end"), m_endAngle); + + // convert to a negative to account for our reversed Y axis (Needed for X and Z ???) + m_startAngle *= -1; + m_endAngle *= -1; + + const char *centerPos = node->Attribute("center"); + if (centerPos) + { + if (strcmpi(centerPos, "auto") == 0) + m_autoCenter = true; + else + { + vector<CStdString> commaSeparated; + StringUtils::SplitString(centerPos, ",", commaSeparated); + if (commaSeparated.size() > 1) + g_SkinInfo.ResolveConstant(commaSeparated[1], m_center.y); + g_SkinInfo.ResolveConstant(commaSeparated[0], m_center.x); + } + } +} + +void CRotateEffect::ApplyEffect(float offset, const CPoint ¢er) +{ + static const float degree_to_radian = 0.01745329252f; + if (m_autoCenter) + m_center = center; + if (m_effect == EFFECT_TYPE_ROTATE_X) + m_matrix.SetXRotation(((m_endAngle - m_startAngle)*offset + m_startAngle) * degree_to_radian, m_center.x, m_center.y, 1.0f); + else if (m_effect == EFFECT_TYPE_ROTATE_Y) + m_matrix.SetYRotation(((m_endAngle - m_startAngle)*offset + m_startAngle) * degree_to_radian, m_center.x, m_center.y, 1.0f); + else if (m_effect == EFFECT_TYPE_ROTATE_Z) // note coordinate aspect ratio is not generally square in the XY plane, so correct for it. + m_matrix.SetZRotation(((m_endAngle - m_startAngle)*offset + m_startAngle) * degree_to_radian, m_center.x, m_center.y, g_graphicsContext.GetScalingPixelRatio()); +} + +CZoomEffect::CZoomEffect(const TiXmlElement *node, const FRECT &rect) : CAnimEffect(node, EFFECT_TYPE_ZOOM) +{ + // effect defaults + m_startX = m_startY = 100; + m_endX = m_endY = 100; + m_center = CPoint(0,0); + m_autoCenter = false; + + float startPosX = rect.left; + float startPosY = rect.top; + float endPosX = rect.left; + float endPosY = rect.top; + + float width = (rect.right) ? rect.right : 0.001f; + float height = (rect.bottom) ? rect.bottom : 0.001f; + + const char *start = node->Attribute("start"); + if (start) + { + CStdStringArray params; + StringUtils::SplitString(start, ",", params); + if (params.size() == 1) + { + g_SkinInfo.ResolveConstant(params[0], m_startX); + m_startY = m_startX; + } + else if (params.size() == 2) + { + g_SkinInfo.ResolveConstant(params[0], m_startX); + g_SkinInfo.ResolveConstant(params[1], m_startY); + } + else if (params.size() == 4) + { // format is start="x,y,width,height" + // use width and height from our rect to calculate our sizing + g_SkinInfo.ResolveConstant(params[0], startPosX); + g_SkinInfo.ResolveConstant(params[1], startPosY); + g_SkinInfo.ResolveConstant(params[2], m_startX); + g_SkinInfo.ResolveConstant(params[3], m_startY); + m_startX *= 100.0f / width; + m_startY *= 100.0f / height; + } + } + const char *end = node->Attribute("end"); + if (end) + { + CStdStringArray params; + StringUtils::SplitString(end, ",", params); + if (params.size() == 1) + { + g_SkinInfo.ResolveConstant(params[0], m_endX); + m_endY = m_endX; + } + else if (params.size() == 2) + { + g_SkinInfo.ResolveConstant(params[0], m_endX); + g_SkinInfo.ResolveConstant(params[1], m_endY); + } + else if (params.size() == 4) + { // format is start="x,y,width,height" + // use width and height from our rect to calculate our sizing + g_SkinInfo.ResolveConstant(params[0], endPosX); + g_SkinInfo.ResolveConstant(params[1], endPosY); + g_SkinInfo.ResolveConstant(params[2], m_endX); + g_SkinInfo.ResolveConstant(params[3], m_endY); + m_endX *= 100.0f / width; + m_endY *= 100.0f / height; + } + } + const char *centerPos = node->Attribute("center"); + if (centerPos) + { + if (strcmpi(centerPos, "auto") == 0) + m_autoCenter = true; + else + { + vector<CStdString> commaSeparated; + StringUtils::SplitString(centerPos, ",", commaSeparated); + if (commaSeparated.size() > 1) + g_SkinInfo.ResolveConstant(commaSeparated[1], m_center.y); + g_SkinInfo.ResolveConstant(commaSeparated[0], m_center.x); + } + } + else + { // no center specified + // calculate the center position... + if (m_startX) + { + float scale = m_endX / m_startX; + if (scale != 1) + m_center.x = (endPosX - scale*startPosX) / (1 - scale); + } + if (m_startY) + { + float scale = m_endY / m_startY; + if (scale != 1) + m_center.y = (endPosY - scale*startPosY) / (1 - scale); + } + } +} + +void CZoomEffect::ApplyEffect(float offset, const CPoint ¢er) +{ + if (m_autoCenter) + m_center = center; + float scaleX = ((m_endX - m_startX)*offset + m_startX) * 0.01f; + float scaleY = ((m_endY - m_startY)*offset + m_startY) * 0.01f; + m_matrix.SetScaler(scaleX, scaleY, m_center.x, m_center.y); +} + +CAnimation::CAnimation() +{ + m_type = ANIM_TYPE_NONE; + m_reversible = true; + m_condition = 0; + m_repeatAnim = ANIM_REPEAT_NONE; + m_currentState = ANIM_STATE_NONE; + m_currentProcess = ANIM_PROCESS_NONE; + m_queuedProcess = ANIM_PROCESS_NONE; + m_lastCondition = false; + m_length = 0; + m_delay = 0; + m_start = 0; + m_amount = 0; +} + +CAnimation::CAnimation(const CAnimation &src) +{ + *this = src; +} + +CAnimation::~CAnimation() +{ + for (unsigned int i = 0; i < m_effects.size(); i++) + delete m_effects[i]; + m_effects.clear(); +} + +const CAnimation &CAnimation::operator =(const CAnimation &src) +{ + if (this == &src) return *this; // same + m_type = src.m_type; + m_reversible = src.m_reversible; + m_condition = src.m_condition; + m_repeatAnim = src.m_repeatAnim; + m_lastCondition = src.m_lastCondition; + m_queuedProcess = src.m_queuedProcess; + m_currentProcess = src.m_currentProcess; + m_currentState = src.m_currentState; + m_start = src.m_start; + m_length = src.m_length; + m_delay = src.m_delay; + m_amount = src.m_amount; + // clear all our effects + for (unsigned int i = 0; i < m_effects.size(); i++) + delete m_effects[i]; + m_effects.clear(); + // and assign the others across + for (unsigned int i = 0; i < src.m_effects.size(); i++) + { + CAnimEffect *newEffect = NULL; + if (src.m_effects[i]->GetType() == CAnimEffect::EFFECT_TYPE_FADE) + newEffect = new CFadeEffect(*(CFadeEffect *)src.m_effects[i]); + else if (src.m_effects[i]->GetType() == CAnimEffect::EFFECT_TYPE_ZOOM) + newEffect = new CZoomEffect(*(CZoomEffect *)src.m_effects[i]); + else if (src.m_effects[i]->GetType() == CAnimEffect::EFFECT_TYPE_SLIDE) + newEffect = new CSlideEffect(*(CSlideEffect *)src.m_effects[i]); + else if (src.m_effects[i]->GetType() == CAnimEffect::EFFECT_TYPE_ROTATE_X || + src.m_effects[i]->GetType() == CAnimEffect::EFFECT_TYPE_ROTATE_Y || + src.m_effects[i]->GetType() == CAnimEffect::EFFECT_TYPE_ROTATE_Z) + newEffect = new CRotateEffect(*(CRotateEffect *)src.m_effects[i]); + if (newEffect) + m_effects.push_back(newEffect); + } + return *this; +} + +void CAnimation::Animate(unsigned int time, bool startAnim) +{ + // First start any queued animations + if (m_queuedProcess == ANIM_PROCESS_NORMAL) + { + if (m_currentProcess == ANIM_PROCESS_REVERSE) + m_start = time - m_amount; // reverse direction of animation + else + m_start = time; + m_currentProcess = ANIM_PROCESS_NORMAL; + } + else if (m_queuedProcess == ANIM_PROCESS_REVERSE) + { + if (m_currentProcess == ANIM_PROCESS_NORMAL) + m_start = time - (m_length - m_amount); // reverse direction of animation + else if (m_currentProcess == ANIM_PROCESS_NONE) + m_start = time; + m_currentProcess = ANIM_PROCESS_REVERSE; + } + // reset the queued state once we've rendered to ensure allocation has occured + if (startAnim || m_queuedProcess == ANIM_PROCESS_REVERSE) + m_queuedProcess = ANIM_PROCESS_NONE; + + // Update our animation process + if (m_currentProcess == ANIM_PROCESS_NORMAL) + { + if (time - m_start < m_delay) + { + m_amount = 0; + m_currentState = ANIM_STATE_DELAYED; + } + else if (time - m_start < m_length + m_delay) + { + m_amount = time - m_start - m_delay; + m_currentState = ANIM_STATE_IN_PROCESS; + } + else + { + m_amount = m_length; + if (m_repeatAnim == ANIM_REPEAT_PULSE && m_lastCondition) + { // pulsed anims auto-reverse + m_currentProcess = ANIM_PROCESS_REVERSE; + m_start = time; + } + else if (m_repeatAnim == ANIM_REPEAT_LOOP && m_lastCondition) + { // looped anims start over + m_amount = 0; + m_start = time; + } + else + m_currentState = ANIM_STATE_APPLIED; + } + } + else if (m_currentProcess == ANIM_PROCESS_REVERSE) + { + if (time - m_start < m_length) + { + m_amount = m_length - (time - m_start); + m_currentState = ANIM_STATE_IN_PROCESS; + } + else + { + m_amount = 0; + if (m_repeatAnim == ANIM_REPEAT_PULSE && m_lastCondition) + { // pulsed anims auto-reverse + m_currentProcess = ANIM_PROCESS_NORMAL; + m_start = time; + } + else + m_currentState = ANIM_STATE_APPLIED; + } + } +} + +void CAnimation::ResetAnimation() +{ + m_queuedProcess = ANIM_PROCESS_NONE; + m_currentProcess = ANIM_PROCESS_NONE; + m_currentState = ANIM_STATE_NONE; +} + +void CAnimation::ApplyAnimation() +{ + m_queuedProcess = ANIM_PROCESS_NONE; + if (m_repeatAnim == ANIM_REPEAT_PULSE) + { // pulsed anims auto-reverse + m_amount = m_length; + m_currentProcess = ANIM_PROCESS_REVERSE; + m_currentState = ANIM_STATE_IN_PROCESS; + } + else if (m_repeatAnim == ANIM_REPEAT_LOOP) + { // looped anims start over + m_amount = 0; + m_currentProcess = ANIM_PROCESS_NORMAL; + m_currentState = ANIM_STATE_IN_PROCESS; + } + else + { // set normal process, so that Calculate() knows that we're finishing for zero length effects + // it will be reset in RenderAnimation() + m_currentProcess = ANIM_PROCESS_NORMAL; + m_currentState = ANIM_STATE_APPLIED; + m_amount = m_length; + } + Calculate(CPoint()); +} + +void CAnimation::Calculate(const CPoint ¢er) +{ + for (unsigned int i = 0; i < m_effects.size(); i++) + { + CAnimEffect *effect = m_effects[i]; + if (effect->GetLength()) + effect->Calculate(m_delay + m_amount, center); + else + { // effect has length zero, so either apply complete + if (m_currentProcess == ANIM_PROCESS_NORMAL) + effect->ApplyState(ANIM_STATE_APPLIED, center); + else + effect->ApplyState(ANIM_STATE_NONE, center); + } + } +} + +void CAnimation::RenderAnimation(TransformMatrix &matrix, const CPoint ¢er) +{ + if (m_currentProcess != ANIM_PROCESS_NONE) + Calculate(center); + // If we have finished an animation, reset the animation state + // We do this here (rather than in Animate()) as we need the + // currentProcess information in the UpdateStates() function of the + // window and control classes. + if (m_currentState == ANIM_STATE_APPLIED) + { + m_currentProcess = ANIM_PROCESS_NONE; + m_queuedProcess = ANIM_PROCESS_NONE; + } + if (m_currentState != ANIM_STATE_NONE) + { + for (unsigned int i = 0; i < m_effects.size(); i++) + matrix *= m_effects[i]->GetTransform(); + } +} + +void CAnimation::QueueAnimation(ANIMATION_PROCESS process) +{ + m_queuedProcess = process; +} + +CAnimation *CAnimation::CreateFader(float start, float end, unsigned int delay, unsigned int length) +{ + CAnimation *anim = new CAnimation(); + if (anim) + { + CFadeEffect *effect = new CFadeEffect(start, end, delay, length); + if (effect) + anim->AddEffect(effect); + } + return anim; +} + +void CAnimation::UpdateCondition(int contextWindow, const CGUIListItem *item) +{ + bool condition = g_infoManager.GetBool(m_condition, contextWindow, item); + if (condition && !m_lastCondition) + QueueAnimation(ANIM_PROCESS_NORMAL); + else if (!condition && m_lastCondition) + { + if (m_reversible) + QueueAnimation(ANIM_PROCESS_REVERSE); + else + ResetAnimation(); + } + m_lastCondition = condition; +} + +void CAnimation::SetInitialCondition(int contextWindow) +{ + m_lastCondition = g_infoManager.GetBool(m_condition, contextWindow); + if (m_lastCondition) + ApplyAnimation(); + else + ResetAnimation(); +} + +void CAnimation::Create(const TiXmlElement *node, const FRECT &rect) +{ + if (!node || !node->FirstChild()) + return; + + // conditions and reversibility + const char *condition = node->Attribute("condition"); + if (condition) + m_condition = g_infoManager.TranslateString(condition); + const char *reverse = node->Attribute("reversible"); + if (reverse && strcmpi(reverse, "false") == 0) + m_reversible = false; + + const TiXmlElement *effect = node->FirstChildElement("effect"); + + CStdString type = node->FirstChild()->Value(); + m_type = ANIM_TYPE_CONDITIONAL; + if (effect) // new layout + type = node->Attribute("type"); + + if (type.Left(7).Equals("visible")) m_type = ANIM_TYPE_VISIBLE; + else if (type.Equals("hidden")) m_type = ANIM_TYPE_HIDDEN; + else if (type.Equals("focus")) m_type = ANIM_TYPE_FOCUS; + else if (type.Equals("unfocus")) m_type = ANIM_TYPE_UNFOCUS; + else if (type.Equals("windowopen")) m_type = ANIM_TYPE_WINDOW_OPEN; + else if (type.Equals("windowclose")) m_type = ANIM_TYPE_WINDOW_CLOSE; + // sanity check + if (m_type == ANIM_TYPE_CONDITIONAL) + { + if (!m_condition) + { + CLog::Log(LOGERROR, "Control has invalid animation type (no condition or no type)"); + return; + } + + // pulsed or loop animations + const char *pulse = node->Attribute("pulse"); + if (pulse && strcmpi(pulse, "true") == 0) + m_repeatAnim = ANIM_REPEAT_PULSE; + const char *loop = node->Attribute("loop"); + if (loop && strcmpi(loop, "true") == 0) + m_repeatAnim = ANIM_REPEAT_LOOP; + } + + m_delay = 0xffffffff; + if (!effect) + { // old layout: + // <animation effect="fade" start="0" end="100" delay="10" time="2000" condition="blahdiblah" reversible="false">focus</animation> + CStdString type = node->Attribute("effect"); + AddEffect(type, node, rect); + } + while (effect) + { // new layout: + // <animation type="focus" condition="blahdiblah" reversible="false"> + // <effect type="fade" start="0" end="100" delay="10" time="2000" /> + // ... + // </animation> + CStdString type = effect->Attribute("type"); + AddEffect(type, effect, rect); + effect = effect->NextSiblingElement("effect"); + } +} + +void CAnimation::AddEffect(const CStdString &type, const TiXmlElement *node, const FRECT &rect) +{ + CAnimEffect *effect = NULL; + if (type.Equals("fade")) + effect = new CFadeEffect(node, m_type < 0); + else if (type.Equals("slide")) + effect = new CSlideEffect(node); + else if (type.Equals("rotate")) + effect = new CRotateEffect(node, CAnimEffect::EFFECT_TYPE_ROTATE_Z); + else if (type.Equals("rotatey")) + effect = new CRotateEffect(node, CAnimEffect::EFFECT_TYPE_ROTATE_Y); + else if (type.Equals("rotatex")) + effect = new CRotateEffect(node, CAnimEffect::EFFECT_TYPE_ROTATE_X); + else if (type.Equals("zoom")) + effect = new CZoomEffect(node, rect); + + if (effect) + AddEffect(effect); +} + +void CAnimation::AddEffect(CAnimEffect *effect) +{ + m_effects.push_back(effect); + // our delay is the minimum of all the effect delays + if (effect->GetDelay() < m_delay) + m_delay = effect->GetDelay(); + // our length is the maximum of all the effect lengths + if (effect->GetLength() > m_delay + m_length) + m_length = effect->GetLength() - m_delay; +} diff --git a/guilib/VisibleEffect.h b/guilib/VisibleEffect.h new file mode 100644 index 0000000000..6786f1f473 --- /dev/null +++ b/guilib/VisibleEffect.h @@ -0,0 +1,205 @@ +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +enum ANIMATION_PROCESS { ANIM_PROCESS_NONE = 0, ANIM_PROCESS_NORMAL, ANIM_PROCESS_REVERSE }; +enum ANIMATION_STATE { ANIM_STATE_NONE = 0, ANIM_STATE_DELAYED, ANIM_STATE_IN_PROCESS, ANIM_STATE_APPLIED }; + +// forward definitions + +class TiXmlElement; +class Tweener; +struct FRECT; +class CGUIListItem; + +#include "TransformMatrix.h" // needed for the TransformMatrix member +#include "Geometry.h" // for CPoint +#include "StdString.h" + +enum ANIMATION_TYPE +{ + ANIM_TYPE_UNFOCUS = -3, + ANIM_TYPE_HIDDEN, + ANIM_TYPE_WINDOW_CLOSE, + ANIM_TYPE_NONE, + ANIM_TYPE_WINDOW_OPEN, + ANIM_TYPE_VISIBLE, + ANIM_TYPE_FOCUS, + ANIM_TYPE_CONDITIONAL // for animations triggered by a condition change +}; + +class CAnimEffect +{ +public: + enum EFFECT_TYPE { EFFECT_TYPE_NONE = 0, EFFECT_TYPE_FADE, EFFECT_TYPE_SLIDE, EFFECT_TYPE_ROTATE_X, EFFECT_TYPE_ROTATE_Y, EFFECT_TYPE_ROTATE_Z, EFFECT_TYPE_ZOOM }; + + CAnimEffect(const TiXmlElement *node, EFFECT_TYPE effect); + CAnimEffect(unsigned int delay, unsigned int length, EFFECT_TYPE effect); + CAnimEffect(const CAnimEffect &src); + + virtual ~CAnimEffect(); + const CAnimEffect &operator=(const CAnimEffect &src); + + void Calculate(unsigned int time, const CPoint ¢er); + void ApplyState(ANIMATION_STATE state, const CPoint ¢er); + + unsigned int GetDelay() const { return m_delay; }; + unsigned int GetLength() const { return m_delay + m_length; }; + const TransformMatrix &GetTransform() const { return m_matrix; }; + EFFECT_TYPE GetType() const { return m_effect; }; + +protected: + TransformMatrix m_matrix; + EFFECT_TYPE m_effect; + +private: + virtual void ApplyEffect(float offset, const CPoint ¢er)=0; + + // timing variables + unsigned int m_length; + unsigned int m_delay; + + Tweener *m_pTweener; +}; + +class CFadeEffect : public CAnimEffect +{ +public: + CFadeEffect(const TiXmlElement *node, bool reverseDefaults); + CFadeEffect(float start, float end, unsigned int delay, unsigned int length); + virtual ~CFadeEffect() {}; +private: + virtual void ApplyEffect(float offset, const CPoint ¢er); + + float m_startAlpha; + float m_endAlpha; +}; + +class CSlideEffect : public CAnimEffect +{ +public: + CSlideEffect(const TiXmlElement *node); + virtual ~CSlideEffect() {}; +private: + virtual void ApplyEffect(float offset, const CPoint ¢er); + + float m_startX; + float m_startY; + float m_endX; + float m_endY; +}; + +class CRotateEffect : public CAnimEffect +{ +public: + CRotateEffect(const TiXmlElement *node, EFFECT_TYPE effect); + virtual ~CRotateEffect() {}; +private: + virtual void ApplyEffect(float offset, const CPoint ¢er); + + float m_startAngle; + float m_endAngle; + + bool m_autoCenter; + CPoint m_center; +}; + +class CZoomEffect : public CAnimEffect +{ +public: + CZoomEffect(const TiXmlElement *node, const FRECT &rect); + virtual ~CZoomEffect() {}; +private: + virtual void ApplyEffect(float offset, const CPoint ¢er); + + float m_startX; + float m_startY; + float m_endX; + float m_endY; + + bool m_autoCenter; + CPoint m_center; +}; + +class CAnimation +{ +public: + CAnimation(); + CAnimation(const CAnimation &src); + + virtual ~CAnimation(); + + const CAnimation &operator=(const CAnimation &src); + + static CAnimation *CreateFader(float start, float end, unsigned int delay, unsigned int length); + + void Create(const TiXmlElement *node, const FRECT &rect); + + void Animate(unsigned int time, bool startAnim); + void ResetAnimation(); + void ApplyAnimation(); + inline void RenderAnimation(TransformMatrix &matrix) + { + RenderAnimation(matrix, CPoint()); + } + void RenderAnimation(TransformMatrix &matrix, const CPoint ¢er); + void QueueAnimation(ANIMATION_PROCESS process); + + inline bool IsReversible() const { return m_reversible; }; + inline int GetCondition() const { return m_condition; }; + inline ANIMATION_TYPE GetType() const { return m_type; }; + inline ANIMATION_STATE GetState() const { return m_currentState; }; + inline ANIMATION_PROCESS GetProcess() const { return m_currentProcess; }; + inline ANIMATION_PROCESS GetQueuedProcess() const { return m_queuedProcess; }; + + void UpdateCondition(int contextWindow, const CGUIListItem *item = NULL); + void SetInitialCondition(int contextWindow); + +private: + void Calculate(const CPoint &point); + void AddEffect(const CStdString &type, const TiXmlElement *node, const FRECT &rect); + void AddEffect(CAnimEffect *effect); + + enum ANIM_REPEAT { ANIM_REPEAT_NONE = 0, ANIM_REPEAT_PULSE, ANIM_REPEAT_LOOP }; + + // type of animation + ANIMATION_TYPE m_type; + bool m_reversible; + int m_condition; + + // conditional anims can repeat + ANIM_REPEAT m_repeatAnim; + bool m_lastCondition; + + // state of animation + ANIMATION_PROCESS m_queuedProcess; + ANIMATION_PROCESS m_currentProcess; + ANIMATION_STATE m_currentState; + + // timing of animation + unsigned int m_start; + unsigned int m_length; + unsigned int m_delay; + unsigned int m_amount; + + std::vector<CAnimEffect *> m_effects; +}; diff --git a/guilib/XMLUtils.cpp b/guilib/XMLUtils.cpp new file mode 100644 index 0000000000..83cb226d55 --- /dev/null +++ b/guilib/XMLUtils.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "XMLUtils.h" +#include "Util.h" +#include "FileSystem/SpecialProtocol.h" + +bool XMLUtils::GetHex(const TiXmlNode* pRootNode, const char* strTag, uint32_t& hexValue) +{ + const TiXmlNode* pNode = pRootNode->FirstChild(strTag ); + if (!pNode || !pNode->FirstChild()) return false; + sscanf(pNode->FirstChild()->Value(), "%x", (uint32_t*) &hexValue ); + return true; +} + + +bool XMLUtils::GetUInt(const TiXmlNode* pRootNode, const char* strTag, uint32_t& uintValue) +{ + const TiXmlNode* pNode = pRootNode->FirstChild(strTag ); + if (!pNode || !pNode->FirstChild()) return false; + uintValue = atol(pNode->FirstChild()->Value()); + return true; +} + +bool XMLUtils::GetLong(const TiXmlNode* pRootNode, const char* strTag, long& lLongValue) +{ + const TiXmlNode* pNode = pRootNode->FirstChild(strTag ); + if (!pNode || !pNode->FirstChild()) return false; + lLongValue = atol(pNode->FirstChild()->Value()); + return true; +} + +bool XMLUtils::GetInt(const TiXmlNode* pRootNode, const char* strTag, int& iIntValue) +{ + const TiXmlNode* pNode = pRootNode->FirstChild(strTag ); + if (!pNode || !pNode->FirstChild()) return false; + iIntValue = atoi(pNode->FirstChild()->Value()); + return true; +} + +bool XMLUtils::GetInt(const TiXmlNode* pRootNode, const char* strTag, int &value, const int min, const int max) +{ + if (GetInt(pRootNode, strTag, value)) + { + if (value < min) value = min; + if (value > max) value = max; + return true; + } + return false; +} + +bool XMLUtils::GetFloat(const TiXmlNode* pRootNode, const char* strTag, float& value) +{ + const TiXmlNode* pNode = pRootNode->FirstChild(strTag ); + if (!pNode || !pNode->FirstChild()) return false; + value = (float)atof(pNode->FirstChild()->Value()); + return true; +} + +bool XMLUtils::GetFloat(const TiXmlNode* pRootElement, const char *tagName, float& fValue, const float fMin, const float fMax) +{ + if (GetFloat(pRootElement, tagName, fValue)) + { // check range + if (fValue < fMin) fValue = fMin; + if (fValue > fMax) fValue = fMax; + return true; + } + return false; +} + +bool XMLUtils::GetBoolean(const TiXmlNode* pRootNode, const char* strTag, bool& bBoolValue) +{ + const TiXmlNode* pNode = pRootNode->FirstChild(strTag ); + if (!pNode || !pNode->FirstChild()) return false; + CStdString strEnabled = pNode->FirstChild()->Value(); + strEnabled.ToLower(); + if (strEnabled == "off" || strEnabled == "no" || strEnabled == "disabled" || strEnabled == "false") + bBoolValue = false; + else + { + bBoolValue = true; + if (strEnabled != "on" && strEnabled != "yes" && strEnabled != "enabled" && strEnabled != "true") + return false; // invalid bool switch - it's probably some other string. + } + return true; +} + +bool XMLUtils::GetString(const TiXmlNode* pRootNode, const char* strTag, CStdString& strStringValue) +{ + const TiXmlElement* pElement = pRootNode->FirstChildElement(strTag ); + if (!pElement) return false; + const char* encoded = pElement->Attribute("urlencoded"); + const TiXmlNode* pNode = pElement->FirstChild(); + if (pNode != NULL) + { + strStringValue = pNode->Value(); + if (encoded && stricmp(encoded,"yes") == 0) + CUtil::UrlDecode(strStringValue); + return true; + } + strStringValue.Empty(); + return false; +} + +bool XMLUtils::GetAdditiveString(const TiXmlNode* pRootNode, const char* strTag, + const CStdString& strSeparator, CStdString& strStringValue) +{ + CStdString strTemp; + const TiXmlElement* node = pRootNode->FirstChildElement(strTag); + bool bResult=false; + while (node) + { + if (node->FirstChild()) + { + bResult = true; + strTemp = node->FirstChild()->Value(); + const char* clear=node->Attribute("clear"); + if (strStringValue.IsEmpty() || (clear && stricmp(clear,"true")==0)) + strStringValue = strTemp; + else + strStringValue += strSeparator+strTemp; + } + node = node->NextSiblingElement(strTag); + } + + return bResult; +} + +/*! + Returns true if the encoding of the document is other then UTF-8. + /param strEncoding Returns the encoding of the document. Empty if UTF-8 +*/ +bool XMLUtils::GetEncoding(const TiXmlDocument* pDoc, CStdString& strEncoding) +{ + const TiXmlNode* pNode=NULL; + while ((pNode=pDoc->IterateChildren(pNode)) && pNode->Type()!=TiXmlNode::DECLARATION) {} + if (!pNode) return false; + const TiXmlDeclaration* pDecl=pNode->ToDeclaration(); + if (!pDecl) return false; + strEncoding=pDecl->Encoding(); + if (strEncoding.Equals("UTF-8") || strEncoding.Equals("UTF8")) strEncoding.Empty(); + strEncoding.MakeUpper(); + return !strEncoding.IsEmpty(); // Other encoding then UTF8? +} + +/*! + Returns true if the encoding of the document is specified as as UTF-8 + /param strXML The XML file (embedded in a string) to check. +*/ +bool XMLUtils::HasUTF8Declaration(const CStdString &strXML) +{ + CStdString test = strXML; + test.ToLower(); + // test for the encoding="utf-8" string + if (test.Find("encoding=\"utf-8\"") >= 0) + return true; + // TODO: test for plain UTF8 here? + return false; +} + +bool XMLUtils::GetPath(const TiXmlNode* pRootNode, const char* strTag, CStdString& strStringValue) +{ + const TiXmlElement* pElement = pRootNode->FirstChildElement(strTag); + if (!pElement) return false; + + int pathVersion = 0; + pElement->Attribute("pathversion", &pathVersion); + const char* encoded = pElement->Attribute("urlencoded"); + const TiXmlNode* pNode = pElement->FirstChild(); + if (pNode != NULL) + { + strStringValue = pNode->Value(); + if (encoded && stricmp(encoded,"yes") == 0) + CUtil::UrlDecode(strStringValue); + strStringValue = CSpecialProtocol::ReplaceOldPath(strStringValue, pathVersion); + return true; + } + strStringValue.Empty(); + return false; +} + +void XMLUtils::SetString(TiXmlNode* pRootNode, const char *strTag, const CStdString& strValue) +{ + TiXmlElement newElement(strTag); + TiXmlNode *pNewNode = pRootNode->InsertEndChild(newElement); + if (pNewNode) + { + TiXmlText value(strValue); + pNewNode->InsertEndChild(value); + } +} + +void XMLUtils::SetInt(TiXmlNode* pRootNode, const char *strTag, int value) +{ + CStdString strValue; + strValue.Format("%i", value); + SetString(pRootNode, strTag, strValue); +} + +void XMLUtils::SetLong(TiXmlNode* pRootNode, const char *strTag, long value) +{ + CStdString strValue; + strValue.Format("%l", value); + SetString(pRootNode, strTag, strValue); +} + +void XMLUtils::SetFloat(TiXmlNode* pRootNode, const char *strTag, float value) +{ + CStdString strValue; + strValue.Format("%f", value); + SetString(pRootNode, strTag, strValue); +} + +void XMLUtils::SetBoolean(TiXmlNode* pRootNode, const char *strTag, bool value) +{ + SetString(pRootNode, strTag, value ? "true" : "false"); +} + +void XMLUtils::SetHex(TiXmlNode* pRootNode, const char *strTag, uint32_t value) +{ + CStdString strValue; + strValue.Format("%x", value); + SetString(pRootNode, strTag, strValue); +} + +void XMLUtils::SetPath(TiXmlNode* pRootNode, const char *strTag, const CStdString& strValue) +{ + TiXmlElement newElement(strTag); + newElement.SetAttribute("pathversion", CSpecialProtocol::path_version); + TiXmlNode *pNewNode = pRootNode->InsertEndChild(newElement); + if (pNewNode) + { + TiXmlText value(strValue); + pNewNode->InsertEndChild(value); + } +} diff --git a/guilib/XMLUtils.h b/guilib/XMLUtils.h new file mode 100644 index 0000000000..22a6723e99 --- /dev/null +++ b/guilib/XMLUtils.h @@ -0,0 +1,54 @@ +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "StdString.h" +#include "tinyXML/tinyxml.h" // no use forwarding these, as this class is the main workhorse anyway, + // thus it simplifies the include patterns + +class XMLUtils +{ +public: + static bool HasUTF8Declaration(const CStdString &strXML); + + static bool GetHex(const TiXmlNode* pRootNode, const char* strTag, uint32_t& dwHexValue); + static bool GetUInt(const TiXmlNode* pRootNode, const char* strTag, uint32_t& dwUIntValue); + static bool GetLong(const TiXmlNode* pRootNode, const char* strTag, long& lLongValue); + static bool GetFloat(const TiXmlNode* pRootNode, const char* strTag, float& value); + static bool GetInt(const TiXmlNode* pRootNode, const char* strTag, int& iIntValue); + static bool GetBoolean(const TiXmlNode* pRootNode, const char* strTag, bool& bBoolValue); + static bool GetString(const TiXmlNode* pRootNode, const char* strTag, CStdString& strStringValue); + static bool GetAdditiveString(const TiXmlNode* pRootNode, const char* strTag, const CStdString& strSeparator, CStdString& strStringValue); + static bool GetEncoding(const TiXmlDocument* pDoc, CStdString& strEncoding); + static bool GetPath(const TiXmlNode* pRootNode, const char* strTag, CStdString& strStringValue); + static bool GetFloat(const TiXmlNode* pRootNode, const char* strTag, float& value, const float min, const float max); + static bool GetInt(const TiXmlNode* pRootNode, const char* strTag, int& iIntValue, const int min, const int max); + + static void SetString(TiXmlNode* pRootNode, const char *strTag, const CStdString& strValue); + static void SetInt(TiXmlNode* pRootNode, const char *strTag, int value); + static void SetFloat(TiXmlNode* pRootNode, const char *strTag, float value); + static void SetBoolean(TiXmlNode* pRootNode, const char *strTag, bool value); + static void SetHex(TiXmlNode* pRootNode, const char *strTag, uint32_t value); + static void SetPath(TiXmlNode* pRootNode, const char *strTag, const CStdString& strValue); + static void SetLong(TiXmlNode* pRootNode, const char *strTag, long iValue); +}; + diff --git a/guilib/common/IRServerSuite/IRServerSuite.cpp b/guilib/common/IRServerSuite/IRServerSuite.cpp new file mode 100644 index 0000000000..0e70b368d4 --- /dev/null +++ b/guilib/common/IRServerSuite/IRServerSuite.cpp @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "IRServerSuite.h" +#include "IrssMessage.h" +#include "ButtonTranslator.h" +#include "log.h" +#include "AdvancedSettings.h" + +#define IRSS_PORT 24000 + +CRemoteControl g_RemoteControl; + +CRemoteControl::CRemoteControl() +{ + m_socket = INVALID_SOCKET; + m_bInitialized = false; + m_isConnecting = false; + Reset(); +} + +CRemoteControl::~CRemoteControl() +{ + Close(); +} + +void CRemoteControl::Disconnect() +{ + StopThread(); + Close(); +} + +void CRemoteControl::Close() +{ + m_isConnecting = false; + if (m_socket != INVALID_SOCKET) + { + if (m_bInitialized) + { + m_bInitialized = false; + CIrssMessage message(IRSSMT_UnregisterClient, IRSSMF_Request | IRSSMF_ForceNotRespond); + SendPacket(message); + } + shutdown(m_socket, SD_BOTH); + closesocket(m_socket); + m_socket = INVALID_SOCKET; + } +} + +void CRemoteControl::Reset() +{ + m_isHolding = false; + m_button = 0; +} + +void CRemoteControl::Initialize() +{ + //trying to connect when there is nothing to connect to is kinda slow so kick it off in a thread. + Create(); + SetName("CRemoteControl"); +} + +void CRemoteControl::Process() +{ + int iTries = 1; + DWORD iMsRetryDelay = 5000; + DWORD time = timeGetTime() - iMsRetryDelay; + // try to connect 6 times @ a 5 second interval (30 seconds) + // multiple tries because irss service might be up and running a little later then xbmc on boot. + while (!m_bStop && iTries <= 6) + { + if (timeGetTime() - time >= iMsRetryDelay) + { + time = timeGetTime(); + if (Connect()) + break; + iTries++; + } + Sleep(10); + } +} + +bool CRemoteControl::Connect() +{ + m_socket = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_socket == INVALID_SOCKET) + { + return false; //Couldn't create the socket + } + // Get the local host information + hostent* localHost = gethostbyname(""); + char* localIP = inet_ntoa (*(struct in_addr *)*localHost->h_addr_list); + + SOCKADDR_IN target; + memset(&target, 0, sizeof(SOCKADDR_IN)); + + target.sin_family = AF_INET; + target.sin_addr.s_addr = inet_addr(localIP); + target.sin_port = htons (IRSS_PORT); + + if (connect(m_socket, (SOCKADDR *)&target, sizeof(target)) == SOCKET_ERROR) + { + Close(); + return false; //Couldn't connect, irss not available + } + + u_long iMode = 1; //non-blocking + if (ioctlsocket(m_socket, FIONBIO, &iMode) == SOCKET_ERROR) + { + CLog::Log(LOGERROR, "IRServerSuite: failed to set socket to non-blocking."); + Close(); + return false; + } + + //register + CIrssMessage mess(IRSSMT_RegisterClient, IRSSMF_Request); + if (!SendPacket(mess)) + { + CLog::Log(LOGERROR, "IRServerSuite: failed to send RegisterClient packet."); + return false; + } + m_isConnecting = true; + return true; +} + +bool CRemoteControl::SendPacket(CIrssMessage& message) +{ + int iSize = 0; + char* bytes = message.ToBytes(iSize); + char buffer[4]; + uint32_t len = htonl(iSize); + memcpy(&buffer[0], &len, 4); + bool bResult = WriteN(&buffer[0], 4); + if (bResult) + { + bResult = WriteN(bytes, iSize); + } + delete[] bytes; + if (!bResult) + { + Close(); + return false; + } + return true; +} + + +void CRemoteControl::Update() +{ + if ((!m_bInitialized && !m_isConnecting) || (m_socket == INVALID_SOCKET)) + { + return; + } + + CIrssMessage mess; + if (!ReadPacket(mess)) + { + return; + } + switch (mess.GetType()) + { + case IRSSMT_RegisterClient: + m_isConnecting = false; + if ((mess.GetFlags() & IRSSMF_Success) != IRSSMF_Success) + { + //uh oh, it failed to register + Close(); + CLog::Log(LOGERROR, "IRServerSuite: failed to register XBMC as a client."); + } + else + { + m_bInitialized = true; + //request info about receivers + CIrssMessage mess(IRSSMT_DetectedReceivers, IRSSMF_Request); + if (!SendPacket(mess)) + { + CLog::Log(LOGERROR, "IRServerSuite: failed to send AvailableReceivers packet."); + } + mess.SetType(IRSSMT_AvailableReceivers); + if (!SendPacket(mess)) + { + CLog::Log(LOGERROR, "IRServerSuite: failed to send AvailableReceivers packet."); + } + mess.SetType(IRSSMT_ActiveReceivers); + if (!SendPacket(mess)) + { + CLog::Log(LOGERROR, "IRServerSuite: failed to send AvailableReceivers packet."); + } + } + break; + case IRSSMT_RemoteEvent: + HandleRemoteEvent(mess); + break; + case IRSSMT_Error: + //I suppose the errormessage is in the packet somewhere... + CLog::Log(LOGERROR, "IRServerSuite: we got an error message."); + break; + case IRSSMT_ServerShutdown: + Close(); + break; + case IRSSMT_ServerSuspend: + //should we do something? + break; + case IRSSMT_ServerResume: + //should we do something? + break; + case IRSSMT_AvailableReceivers: + { + uint32_t size = mess.GetDataSize(); + if (size > 0) + { + char* data = mess.GetData(); + char* availablereceivers = new char[size + 1]; + memcpy(availablereceivers, data, size); + availablereceivers[size] = '\0'; + CLog::Log(LOGINFO, "IRServerSuite: Available receivers: %s", availablereceivers); + delete[] availablereceivers; + } + } + break; + case IRSSMT_DetectedReceivers: + { + uint32_t size = mess.GetDataSize(); + if (size > 0) + { + char* data = mess.GetData(); + char* detectedreceivers = new char[size + 1]; + memcpy(detectedreceivers, data, size); + detectedreceivers[size] = '\0'; + CLog::Log(LOGINFO, "IRServerSuite: Detected receivers: %s", detectedreceivers); + delete[] detectedreceivers; + } + } + break; + case IRSSMT_ActiveReceivers: + { + uint32_t size = mess.GetDataSize(); + if (size > 0) + { + char* data = mess.GetData(); + char* activereceivers = new char[size + 1]; + memcpy(activereceivers, data, size); + activereceivers[size] = '\0'; + CLog::Log(LOGINFO, "IRServerSuite: Active receivers: %s", activereceivers); + delete[] activereceivers; + } + } + break; + } +} + +bool CRemoteControl::HandleRemoteEvent(CIrssMessage& message) +{ + try + { + //flag should be notify, maybe check it? + char* data = message.GetData(); + uint32_t datalen = message.GetDataSize(); + char* deviceName; + char* keycode; + uint32_t devicenamelength; + uint32_t keycodelength; + if (datalen == 0) + { + CLog::Log(LOGERROR, "IRServerSuite: no data in remote message."); + return false; + } + if (datalen <= 8) + { + //seems to be version 1.0.4.1, only keycode is sent, use Microsoft MCE mapping?? + devicenamelength = 13; + deviceName = new char[devicenamelength + 1]; + sprintf(deviceName, "Microsoft MCE"); + keycodelength = datalen; + keycode = new char[keycodelength + 1]; + memcpy(keycode, data, keycodelength); + } + else + { + //first 4 bytes is devicename length + memcpy(&devicenamelength, data, 4); + //devicename itself + if (datalen < 4 + devicenamelength) + { + CLog::Log(LOGERROR, "IRServerSuite: invalid data in remote message (size: %u).", datalen); + return false; + } + deviceName = new char[devicenamelength + 1]; + memcpy(deviceName, data + 4, devicenamelength); + if (datalen < 8 + devicenamelength) + { + CLog::Log(LOGERROR, "IRServerSuite: invalid data in remote message (size: %u).", datalen); + return false; + } + //next 4 bytes is keycode length + memcpy(&keycodelength, data + 4 + devicenamelength, 4); + //keycode itself + if (datalen < 8 + devicenamelength + keycodelength) + { + CLog::Log(LOGERROR, "IRServerSuite: invalid data in remote message (size: %u).", datalen); + delete[] deviceName; + return false; + } + keycode = new char[keycodelength + 1]; + memcpy(keycode, data + 8 + devicenamelength, keycodelength); + } + deviceName[devicenamelength] = '\0'; + keycode[keycodelength] = '\0'; + //translate to a buttoncode xbmc understands + m_button = CButtonTranslator::GetInstance().TranslateLircRemoteString(deviceName, keycode); + if (g_advancedSettings.m_logLevel == LOG_LEVEL_DEBUG_FREEMEM) + { + CLog::Log(LOGINFO, "IRServerSuite, RemoteEvent: %s %s", deviceName, keycode); + } + delete[] deviceName; + delete[] keycode; + return true; + } + catch(...) + { + CLog::Log(LOGERROR, "IRServerSuite: exception while processing RemoteEvent."); + return false; + } +} + +int CRemoteControl::ReadN(char *buffer, int n) +{ + int nOriginalSize = n; + memset(buffer, 0, n); + char *ptr = buffer; + while (n > 0) + { + int nBytes = 0; + nBytes = recv(m_socket, ptr, n, 0); + + if (WSAGetLastError() == WSAEWOULDBLOCK) + { + return nOriginalSize - n; + } + if (nBytes < 0) + { + if (!m_isConnecting) + { + CLog::Log(LOGERROR, "%s, IRServerSuite recv error %d", __FUNCTION__, GetLastError()); + } + Close(); + return -1; + } + + if (nBytes == 0) + { + CLog::Log(LOGDEBUG,"%s, IRServerSuite socket closed by server", __FUNCTION__); + Close(); + break; + } + + n -= nBytes; + ptr += nBytes; + } + + return nOriginalSize - n; +} + +bool CRemoteControl::WriteN(const char *buffer, int n) +{ + const char *ptr = buffer; + while (n > 0) + { + int nBytes = send(m_socket, ptr, n, 0); + if (nBytes < 0) + { + CLog::Log(LOGERROR, "%s, IRServerSuite send error %d (%d bytes)", __FUNCTION__, GetLastError(), n); + Close(); + return false; + } + + if (nBytes == 0) + break; + + n -= nBytes; + ptr += nBytes; + } + + return n == 0; +} + +bool CRemoteControl::ReadPacket(CIrssMessage &message) +{ + try + { + char sizebuf[4]; + int iRead = ReadN(&sizebuf[0], 4); + if (iRead <= 0) return false; //nothing to read + if (iRead != 4) + { + CLog::Log(LOGERROR, "IRServerSuite: failed to read packetsize."); + return false; + } + uint32_t size = 0; + memcpy(&size, &sizebuf[0], 4); + size = ntohl(size); + char* messagebytes = new char[size]; + if (ReadN(messagebytes, size) != size) + { + CLog::Log(LOGERROR, "IRServerSuite: failed to read packet."); + return false; + } + if (!CIrssMessage::FromBytes(messagebytes, size, message)) + { + CLog::Log(LOGERROR, "IRServerSuite: invalid packet received (size: %u).", size); + return false; + } + delete[] messagebytes; + return true; + } + catch(...) + { + CLog::Log(LOGERROR, "IRServerSuite: exception while processing packet."); + return false; + } +} + +WORD CRemoteControl::GetButton() +{ + return m_button; +} + +bool CRemoteControl::IsHolding() +{ + return m_isHolding; +} diff --git a/guilib/common/IRServerSuite/IRServerSuite.h b/guilib/common/IRServerSuite/IRServerSuite.h new file mode 100644 index 0000000000..d2cb322640 --- /dev/null +++ b/guilib/common/IRServerSuite/IRServerSuite.h @@ -0,0 +1,64 @@ +#pragma once +/* + * Copyright (C) 2005-2008 Team XBMC + * http://xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include <winsock2.h> +#include "StdString.h" +#include "IrssMessage.h" +#include "Thread.h" + +class CRemoteControl : CThread +{ +public: + CRemoteControl(); + ~CRemoteControl(); + void Initialize(); + void Disconnect(); + void Reset(); + void Update(); + WORD GetButton(); + bool IsHolding(); + bool IsInitialized() {return m_bInitialized;} + bool IsInUse() {return false;} + +protected: + virtual void Process(); + +private: + WORD m_button; + bool m_isHolding; + bool m_bInitialized; + SOCKET m_socket; + bool m_isConnecting; + CStdString m_deviceName; + CStdString m_keyCode; + + bool SendPacket(CIrssMessage& message); + bool ReadPacket(CIrssMessage& message); + int ReadN(char *buffer, int n); + bool WriteN(const char *buffer, int n); + bool Connect(); + void Close(); + + bool HandleRemoteEvent(CIrssMessage& message); +}; + +extern CRemoteControl g_RemoteControl; diff --git a/guilib/common/IRServerSuite/IrssMessage.cpp b/guilib/common/IRServerSuite/IrssMessage.cpp new file mode 100644 index 0000000000..7f8d40b174 --- /dev/null +++ b/guilib/common/IRServerSuite/IrssMessage.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2005-2008 Team XBMC + * http://xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "IrssMessage.h" + +CIrssMessage::CIrssMessage() +{ + m_type = IRSSMT_Unknown; + m_flags = 0; + m_data = NULL; + m_dataSize = 0; +} + +CIrssMessage::CIrssMessage(IRSS_MessageType type, uint32_t flags) +{ + m_type = type; + m_flags = flags; + m_data = NULL; + m_dataSize = 0; +} + +CIrssMessage::CIrssMessage(IRSS_MessageType type, uint32_t flags, char* data, int size) +{ + m_type = type; + m_flags = flags; + SetDataAsBytes(data, size); +} + +CIrssMessage::CIrssMessage(IRSS_MessageType type, uint32_t flags, const CStdString& data) +{ + m_type = type; + m_flags = flags; + SetDataAsString(data); +} + +CIrssMessage::~CIrssMessage() +{ + FreeData(); +} + +void CIrssMessage::SetDataAsBytes(char* data, int size) +{ + if (!data) + { + FreeData(); + } + else + { + m_data = (char*)malloc(size * sizeof(char)); + memcpy(m_data, data, size); + m_dataSize = size; + } +} + +void CIrssMessage::SetDataAsString(const CStdString& data) +{ + if (!data || data.IsEmpty()) + { + FreeData(); + } + else + { + m_data = strdup(data.c_str()); + m_dataSize = strlen(data.c_str()); + } +} + +void CIrssMessage::FreeData() +{ + free(m_data); + m_data = NULL; + m_dataSize = 0; +} + +char* CIrssMessage::ToBytes(int& size) +{ + int dataLength = 0; + if (m_data) + { + dataLength = m_dataSize; + } + + size = 8 + dataLength; + char* byteArray = new char[size]; + + memcpy(&byteArray[0], &m_type, 4); + memcpy(&byteArray[4], &m_flags, 4); + + if (m_data) + { + memcpy(&byteArray[8], &m_data, m_dataSize); + } + + return byteArray; +} + +bool CIrssMessage::FromBytes(char* from, int size, CIrssMessage& message) +{ + if (!from) + return false; + + if (size < 8) + return false; + + //IRSS_MessageType type = (MessageType)BitConverter.ToInt32(from, 0); + //IRSS_MessageFlags flags = (MessageFlags)BitConverter.ToInt32(from, 4); + uint32_t type; + memcpy(&type, from, 4); + uint32_t flags; + memcpy(&flags, from + 4, 4); + + message.SetType((IRSS_MessageType)type); + message.SetFlags(flags); + if (size > 8) + { + message.SetDataAsBytes(from + 8, size - 8); + } + return true; +} + +void CIrssMessage::SetType(IRSS_MessageType type) +{ + m_type = type; +} +void CIrssMessage::SetFlags(uint32_t flags) +{ + m_flags = flags; +} diff --git a/guilib/common/IRServerSuite/IrssMessage.h b/guilib/common/IRServerSuite/IrssMessage.h new file mode 100644 index 0000000000..d5ddefa0a7 --- /dev/null +++ b/guilib/common/IRServerSuite/IrssMessage.h @@ -0,0 +1,209 @@ +#pragma once +/* + * Copyright (C) 2005-2008 Team XBMC + * http://xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "StdString.h" + + /// <summary> + /// Type of message. + /// </summary> + enum IRSS_MessageType + { + /// <summary> + /// Unknown message type. + /// </summary> + IRSSMT_Unknown = 0, + + /// <summary> + /// Register Client. + /// </summary> + IRSSMT_RegisterClient = 1, + /// <summary> + /// Unregister Client. + /// </summary> + IRSSMT_UnregisterClient = 2, + + /// <summary> + /// Register Repeater. + /// </summary> + IRSSMT_RegisterRepeater = 3, + /// <summary> + /// Unregister Repeater. + /// </summary> + IRSSMT_UnregisterRepeater = 4, + + /// <summary> + /// Learn IR Command. + /// </summary> + IRSSMT_LearnIR = 5, + /// <summary> + /// Blast IR Command. + /// </summary> + IRSSMT_BlastIR = 6, + + /// <summary> + /// Error. + /// </summary> + IRSSMT_Error = 7, + + /// <summary> + /// Server Shutdown. + /// </summary> + IRSSMT_ServerShutdown = 8, + /// <summary> + /// Server Suspend. + /// </summary> + IRSSMT_ServerSuspend = 9, + /// <summary> + /// Server Resume + /// </summary> + IRSSMT_ServerResume = 10, + + /// <summary> + /// Remote Event. + /// </summary> + IRSSMT_RemoteEvent = 11, + /// <summary> + /// Keyboard Event. + /// </summary> + IRSSMT_KeyboardEvent = 12, + /// <summary> + /// Mouse Event. + /// </summary> + IRSSMT_MouseEvent = 13, + + /// <summary> + /// Forward a Remote Event. + /// </summary> + IRSSMT_ForwardRemoteEvent = 14, + /// <summary> + /// Forward a Keyboard Event. + /// </summary> + IRSSMT_ForwardKeyboardEvent = 15, + /// <summary> + /// Forward a Mouse Event. + /// </summary> + IRSSMT_ForwardMouseEvent = 16, + + /// <summary> + /// Available Receivers. + /// </summary> + IRSSMT_AvailableReceivers = 17, + /// <summary> + /// Available Blasters. + /// </summary> + IRSSMT_AvailableBlasters = 18, + /// <summary> + /// Active Receivers. + /// </summary> + IRSSMT_ActiveReceivers = 19, + /// <summary> + /// Active Blasters. + /// </summary> + IRSSMT_ActiveBlasters = 20, + /// <summary> + /// Detected Receivers. + /// </summary> + IRSSMT_DetectedReceivers = 21, + /// <summary> + /// Detected Blasters. + /// </summary> + IRSSMT_DetectedBlasters = 22, + }; + + /// <summary> + /// Flags to determine more information about the message. + /// </summary> + enum IRSS_MessageFlags + { + /// <summary> + /// No Flags. + /// </summary> + IRSSMF_None = 0x0000, + + /// <summary> + /// Message is a Request. + /// </summary> + IRSSMF_Request = 0x0001, + /// <summary> + /// Message is a Response to a received Message. + /// </summary> + IRSSMF_Response = 0x0002, + /// <summary> + /// Message is a Notification. + /// </summary> + IRSSMF_Notify = 0x0004, + + /// <summary> + /// Operation Success. + /// </summary> + IRSSMF_Success = 0x0008, + /// <summary> + /// Operation Failure. + /// </summary> + IRSSMF_Failure = 0x0010, + /// <summary> + /// Operation Time-Out. + /// </summary> + IRSSMF_Timeout = 0x0020, + + //IRSSMF_Error = 0x0040, + + //IRSSMF_DataString = 0x0080, + //IRSSMF_DataBytes = 0x0100, + + //IRSSMF_ForceRespond = 0x0200, + + /// <summary> + /// Force the recipient not to respond. + /// </summary> + IRSSMF_ForceNotRespond = 0x0400, + }; + +class CIrssMessage +{ +public: + CIrssMessage(); + CIrssMessage(IRSS_MessageType type, uint32_t flags); + CIrssMessage(IRSS_MessageType type, uint32_t flags, char* data, int size); + CIrssMessage(IRSS_MessageType type, uint32_t flags, const CStdString& data); + ~CIrssMessage(); + + void SetDataAsBytes(char* data, int size); + void SetDataAsString(const CStdString& data); + char* ToBytes(int& size); + void SetType(IRSS_MessageType type); + void SetFlags(uint32_t flags); + IRSS_MessageType GetType() {return m_type;} + uint32_t GetFlags() {return m_flags;} + char* GetData() {return m_data;} + uint32_t GetDataSize() {return m_dataSize;} + static bool FromBytes(char* from, int size, CIrssMessage& message); + +private: + IRSS_MessageType m_type; + uint32_t m_flags; + + char* m_data; + int m_dataSize; + + void FreeData(); +}; diff --git a/guilib/common/LIRC.cpp b/guilib/common/LIRC.cpp new file mode 100644 index 0000000000..9b7bf2dcd2 --- /dev/null +++ b/guilib/common/LIRC.cpp @@ -0,0 +1,257 @@ +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/inotify.h> +#include <limits.h> +#include <unistd.h> +#include "LIRC.h" +#include "ButtonTranslator.h" +#include "log.h" +#include "AdvancedSettings.h" +#include "FileSystem/File.h" + +#define LIRC_DEVICE "/dev/lircd" + +CRemoteControl g_RemoteControl; + +CRemoteControl::CRemoteControl() +{ + m_fd = -1; + m_file = NULL; + m_bInitialized = false; + m_skipHold = false; + m_button = 0; + m_isHolding = false; + m_used = true; + m_deviceName = LIRC_DEVICE; + m_inotify_fd = -1; + m_inotify_wd = -1; + m_bLogConnectFailure = true; + m_lastInitAttempt = -5000; + m_initRetryPeriod = 5000; + Reset(); +} + +CRemoteControl::~CRemoteControl() +{ + if (m_file != NULL) + fclose(m_file); +} + +void CRemoteControl::setUsed(bool value) +{ + m_used=value; + if (!value) + CLog::Log(LOGINFO, "LIRC %s: disabled", __FUNCTION__); +} + +void CRemoteControl::Reset() +{ + m_isHolding = false; + m_button = 0; +} + +void CRemoteControl::Disconnect() +{ + if (!m_used) + return; + + if (m_fd != -1) + { + m_bInitialized = false; + if (m_file != NULL) + fclose(m_file); + m_fd = -1; + m_file = NULL; + if (m_inotify_wd >= 0) { + inotify_rm_watch(m_inotify_fd, m_inotify_wd); + m_inotify_wd = -1; + } + if (m_inotify_fd >= 0) + close(m_inotify_fd); + } +} + +void CRemoteControl::setDeviceName(const CStdString& value) +{ + if (value.length()>0) + m_deviceName=value; + else + m_deviceName=LIRC_DEVICE; +} + +void CRemoteControl::Initialize() +{ + struct sockaddr_un addr; + int now = timeGetTime(); + + if (!m_used || now < m_lastInitAttempt + m_initRetryPeriod) + return; + m_lastInitAttempt = now; + + if (!XFILE::CFile::Exists(m_deviceName)) { + m_initRetryPeriod *= 2; + if (m_initRetryPeriod > 60000) + { + m_used = false; + CLog::Log(LOGDEBUG, "LIRC device %s does not exist. Giving up.", m_deviceName.c_str()); + } + else + CLog::Log(LOGDEBUG, "LIRC device %s does not exist. Retry in %ds.", m_deviceName.c_str(), m_initRetryPeriod/1000); + return; + } + + m_initRetryPeriod = 5000; + + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, m_deviceName.c_str()); + + // Open the socket from which we will receive the remote commands + if ((m_fd = socket(AF_UNIX, SOCK_STREAM, 0)) != -1) + { + // Connect to the socket + if (connect(m_fd, (struct sockaddr *)&addr, sizeof(addr)) != -1) + { + int opts; + m_bLogConnectFailure = true; + if ((opts = fcntl(m_fd,F_GETFL)) != -1) + { + // Set the socket to non-blocking + opts = (opts | O_NONBLOCK); + if (fcntl(m_fd,F_SETFL,opts) != -1) + { + if ((m_file = fdopen(m_fd, "r")) != NULL) + { + // Setup inotify so we can disconnect if lircd is restarted + if ((m_inotify_fd = inotify_init()) >= 0) + { + // Set the fd non-blocking + if ((opts = fcntl(m_inotify_fd, F_GETFL)) != -1) + { + opts |= O_NONBLOCK; + if (fcntl(m_inotify_fd, F_SETFL, opts) != -1) + { + // Set an inotify watch on the lirc device + if ((m_inotify_wd = inotify_add_watch(m_inotify_fd, m_deviceName.c_str(), IN_DELETE_SELF)) != -1) + { + m_bInitialized = true; + CLog::Log(LOGINFO, "LIRC %s: sucessfully started on: %s", __FUNCTION__, addr.sun_path); + } + else + CLog::Log(LOGDEBUG, "LIRC: Failed to initialize Inotify. LIRC device will not be monitored."); + } + } + } + } + else + CLog::Log(LOGERROR, "LIRC %s: fdopen failed: %s", __FUNCTION__, strerror(errno)); + } + else + CLog::Log(LOGERROR, "LIRC %s: fcntl(F_SETFL) failed: %s", __FUNCTION__, strerror(errno)); + } + else + CLog::Log(LOGERROR, "LIRC %s: fcntl(F_GETFL) failed: %s", __FUNCTION__, strerror(errno)); + } + else + { + if (m_bLogConnectFailure) + { + CLog::Log(LOGINFO, "LIRC %s: connect failed: %s", __FUNCTION__, strerror(errno)); + m_bLogConnectFailure = false; + } + } + } + else + CLog::Log(LOGINFO, "LIRC %s: socket failed: %s", __FUNCTION__, strerror(errno)); + if (!m_bInitialized) + Disconnect(); +} + +bool CRemoteControl::CheckDevice() { + if (m_inotify_fd < 0 || m_inotify_wd < 0) + return true; // inotify wasn't setup for some reason, assume all is well + int bufsize = sizeof(struct inotify_event) + PATH_MAX; + char buf[bufsize]; + int ret = read(m_inotify_fd, buf, bufsize); + for (int i = 0; i + (int)sizeof(struct inotify_event) <= ret;) { + struct inotify_event* e = (struct inotify_event*)(buf+i); + if (e->mask & IN_DELETE_SELF) { + CLog::Log(LOGDEBUG, "LIRC device removed, disconnecting..."); + Disconnect(); + return false; + } + i += sizeof(struct inotify_event)+e->len; + } + return true; +} + +void CRemoteControl::Update() +{ + if (!m_bInitialized || !m_used ) + return; + + if (!CheckDevice()) + return; + + Uint32 now = SDL_GetTicks(); + + // Read a line from the socket + while (fgets(m_buf, sizeof(m_buf), m_file) != NULL) + { + // Remove the \n + m_buf[strlen(m_buf)-1] = '\0'; + + // Parse the result. Sample line: + // 000000037ff07bdd 00 OK mceusb + char scanCode[128]; + char buttonName[128]; + char repeatStr[4]; + char deviceName[128]; + sscanf(m_buf, "%s %s %s %s", &scanCode[0], &repeatStr[0], &buttonName[0], &deviceName[0]); + + // Some template LIRC configuration have button names in apostrophes or quotes. + // If we got a quoted button name, strip 'em + unsigned int buttonNameLen = strlen(buttonName); + if ( buttonNameLen > 2 + && ( (buttonName[0] == '\'' && buttonName[buttonNameLen-1] == '\'') + || ((buttonName[0] == '"' && buttonName[buttonNameLen-1] == '"') ) ) ) + { + memmove( buttonName, buttonName + 1, buttonNameLen - 2 ); + buttonName[ buttonNameLen - 2 ] = '\0'; + } + + m_button = CButtonTranslator::GetInstance().TranslateLircRemoteString(deviceName, buttonName); + + if (strcmp(repeatStr, "00") == 0) + { + CLog::Log(LOGDEBUG, "LIRC: %s - NEW at %d:%s (%s)", __FUNCTION__, now, m_buf, buttonName); + m_firstClickTime = now; + m_isHolding = false; + m_skipHold = true; + return; + } + else if (now - m_firstClickTime >= (Uint32) g_advancedSettings.m_remoteRepeat && !m_skipHold) + { + m_isHolding = true; + } + else + { + m_isHolding = false; + m_button = 0; + } + } + if (feof(m_file) != 0) + Disconnect(); + m_skipHold = false; +} + +WORD CRemoteControl::GetButton() +{ + return m_button; +} + +bool CRemoteControl::IsHolding() +{ + return m_isHolding; +} diff --git a/guilib/common/LIRC.h b/guilib/common/LIRC.h new file mode 100644 index 0000000000..fc6882ab6d --- /dev/null +++ b/guilib/common/LIRC.h @@ -0,0 +1,44 @@ +#ifndef LIRC_H +#define LIRC_H + +#include "../system.h" +#include "StdString.h" + +class CRemoteControl +{ +public: + CRemoteControl(); + ~CRemoteControl(); + void Initialize(); + void Disconnect(); + void Reset(); + void Update(); + WORD GetButton(); + bool IsHolding(); + void setDeviceName(const CStdString& value); + void setUsed(bool value); + bool IsInUse() const { return m_used; } + bool IsInitialized() const { return m_bInitialized; } + +private: + int m_fd; + int m_inotify_fd; + int m_inotify_wd; + int m_lastInitAttempt; + int m_initRetryPeriod; + FILE* m_file; + bool m_isHolding; + WORD m_button; + char m_buf[128]; + bool m_bInitialized; + bool m_skipHold; + bool m_used; + bool m_bLogConnectFailure; + Uint32 m_firstClickTime; + CStdString m_deviceName; + bool CheckDevice(); +}; + +extern CRemoteControl g_RemoteControl; + +#endif diff --git a/guilib/common/Makefile.in b/guilib/common/Makefile.in new file mode 100644 index 0000000000..757c468768 --- /dev/null +++ b/guilib/common/Makefile.in @@ -0,0 +1,14 @@ +ARCH=@ARCH@ + +INCLUDES=-I. -I../ -I../../xbmc -I../../xbmc/linux -I../../xbmc/utils + +ifeq ($(findstring osx,$(ARCH)), osx) +SRCS=SDLJoystick.cpp +else +SRCS=SDLJoystick.cpp LIRC.cpp +endif + +LIB=gui_common.a + +include ../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) diff --git a/guilib/common/SDLJoystick.cpp b/guilib/common/SDLJoystick.cpp new file mode 100644 index 0000000000..9ec94f5040 --- /dev/null +++ b/guilib/common/SDLJoystick.cpp @@ -0,0 +1,388 @@ +#include "system.h" +#include "../Key.h" +#include "SDLJoystick.h" +#include "ButtonTranslator.h" +#include "utils/log.h" + +#include <math.h> + +#ifdef HAS_SDL_JOYSTICK + +using namespace std; + +CJoystick g_Joystick; // global + +CJoystick::CJoystick() +{ + Reset(); + m_NumAxes = 0; + m_AxisId = 0; + m_JoyId = 0; + m_ButtonId = 0; + m_HatId = 0; + m_HatState = SDL_HAT_CENTERED; + m_ActiveFlags = JACTIVE_NONE; + for (int i = 0 ; i<MAX_AXES ; i++) + m_Amount[i] = 0; + + SetSafeRange(2000); +} + +void CJoystick::Initialize(HWND hWnd) +{ + // clear old joystick names + m_JoystickNames.clear(); + + // any open ones? if so, close them. + if (m_Joysticks.size()>0) + { + for(size_t idJoy = 0; idJoy < m_Joysticks.size(); idJoy++) + { + // any joysticks unplugged? + if(SDL_JoystickOpened(idJoy)) + SDL_JoystickClose(m_Joysticks[idJoy]); + } + m_Joysticks.clear(); + m_JoyId = -1; + } + + // any joysticks connected? + if (SDL_NumJoysticks()>0) + { + // load joystick names and open all connected joysticks + for (int i = 0 ; i<SDL_NumJoysticks() ; i++) + { + SDL_Joystick *joy = SDL_JoystickOpen(i); + +#ifdef __APPLE__ + // On OS X, the 360 controllers are handled externally, since the SDL code is + // really buggy and doesn't handle disconnects. + // + if (std::string(SDL_JoystickName(i)).find("360") != std::string::npos) + { + CLog::Log(LOGNOTICE, "Ignoring joystick: %s", SDL_JoystickName(i)); + continue; + } +#endif + + m_Joysticks.push_back(joy); + if (joy) + { + CalibrateAxis(joy); + m_JoystickNames.push_back(string(SDL_JoystickName(i))); + CLog::Log(LOGNOTICE, "Enabled Joystick: %s", SDL_JoystickName(i)); + } + else + { + m_JoystickNames.push_back(string("")); + } + } + } + + // disable joystick events, since we'll be polling them + SDL_JoystickEventState(SDL_DISABLE); +} + +void CJoystick::CalibrateAxis(SDL_Joystick* joy) +{ + SDL_JoystickUpdate(); + + int numax = SDL_JoystickNumAxes(joy); + numax = (numax>MAX_AXES)?MAX_AXES:numax; + + // get default axis states + for (int a = 0 ; a<numax ; a++) + { + m_DefaultAmount[a+1] = SDL_JoystickGetAxis(joy, a); + CLog::Log(LOGDEBUG, "Calibrated Axis: %d , default amount %d\n", a, m_DefaultAmount[a+1]); + } +} + +void CJoystick::Reset(bool axis) +{ + if (axis) + { + SetAxisActive(false); + for (int i = 0 ; i<MAX_AXES ; i++) + { + ResetAxis(i); + } + } +} + +void CJoystick::Update() +{ + int buttonId = -1; + int axisId = -1; + int hatId = -1; + int numj = m_Joysticks.size(); + if (numj <= 0) + return; + + // update the state of all opened joysticks + SDL_JoystickUpdate(); + + // go through all joysticks + for (int j = 0; j<numj; j++) + { + SDL_Joystick *joy = m_Joysticks[j]; + int numb = SDL_JoystickNumButtons(joy); + int numhat = SDL_JoystickNumHats(joy); + int numax = SDL_JoystickNumAxes(joy); + numax = (numax>MAX_AXES)?MAX_AXES:numax; + int axisval; + Uint8 hatval; + + // get button states first, they take priority over axis + for (int b = 0 ; b<numb ; b++) + { + if (SDL_JoystickGetButton(joy, b)) + { + m_JoyId = j; + buttonId = b+1; + j = numj-1; + break; + } + } + + for (int h = 0; h < numhat; h++) + { + hatval = SDL_JoystickGetHat(joy, h); + if (hatval != SDL_HAT_CENTERED) + { + m_JoyId = j; + hatId = h + 1; + m_HatState = hatval; + j = numj-1; + break; + } + } + + // get axis states + m_NumAxes = numax; + for (int a = 0 ; a<numax ; a++) + { + axisval = SDL_JoystickGetAxis(joy, a); + axisId = a+1; + if (axisId<=0 || axisId>=MAX_AXES) + { + CLog::Log(LOGERROR, "Axis Id out of range. Maximum supported axis: %d", MAX_AXES); + } + else + { + m_Amount[axisId] = axisval; //[-32768 to 32767] + if (axisval!=m_DefaultAmount[axisId]) + { + m_JoyId = j; + } + } + } + m_AxisId = GetAxisWithMaxAmount(); + } + + if(hatId==-1) + { + if(m_HatId!=0) + CLog::Log(LOGDEBUG, "Joystick %d hat %u Centered", m_JoyId, hatId); + m_pressTicksHat = 0; + SetHatActive(false); + m_HatId = 0; + } + else + { + if(hatId!=m_HatId) + { + CLog::Log(LOGDEBUG, "Joystick %d hat %u Down", m_JoyId, hatId); + m_HatId = hatId; + m_pressTicksHat = SDL_GetTicks(); + } + SetHatActive(); + } + + if (buttonId==-1) + { + if (m_ButtonId!=0) + { + CLog::Log(LOGDEBUG, "Joystick %d button %d Up", m_JoyId, m_ButtonId); + } + m_pressTicksButton = 0; + SetButtonActive(false); + m_ButtonId = 0; + } + else + { + if (buttonId!=m_ButtonId) + { + CLog::Log(LOGDEBUG, "Joystick %d button %d Down", m_JoyId, buttonId); + m_ButtonId = buttonId; + m_pressTicksButton = SDL_GetTicks(); + } + SetButtonActive(); + } + +} + +void CJoystick::Update(SDL_Event& joyEvent) +{ + int buttonId = -1; + int axisId = -1; + int joyId = -1; + bool ignore = false; // not used for now + bool axis = false; + + printf("JoystickEvent %i\n", joyEvent.type); + + switch(joyEvent.type) + { + case SDL_JOYBUTTONDOWN: + m_JoyId = joyId = joyEvent.jbutton.which; + m_ButtonId = buttonId = joyEvent.jbutton.button + 1; + m_pressTicksButton = SDL_GetTicks(); + SetButtonActive(); + CLog::Log(LOGDEBUG, "Joystick %d button %d Down", joyId, buttonId); + break; + + case SDL_JOYAXISMOTION: + joyId = joyEvent.jaxis.which; + axisId = joyEvent.jaxis.axis + 1; + m_NumAxes = SDL_JoystickNumAxes(m_Joysticks[joyId]); + if (axisId<=0 || axisId>=MAX_AXES) + { + CLog::Log(LOGERROR, "Axis Id out of range. Maximum supported axis: %d", MAX_AXES); + ignore = true; + break; + } + axis = true; + m_JoyId = joyId; + if (joyEvent.jaxis.value==0) + { + ignore = true; + m_Amount[axisId] = 0; + } + else + { + m_Amount[axisId] = joyEvent.jaxis.value; //[-32768 to 32767] + } + m_AxisId = GetAxisWithMaxAmount(); + CLog::Log(LOGDEBUG, "Joystick %d Axis %d Amount %d", joyId, axisId, m_Amount[axisId]); + break; + + case SDL_JOYHATMOTION: + m_JoyId = joyId = joyEvent.jbutton.which; + m_HatId = joyEvent.jhat.hat + 1; + m_pressTicksHat = SDL_GetTicks(); + m_HatState = joyEvent.jhat.value; + SetHatActive(m_HatState != SDL_HAT_CENTERED); + CLog::Log(LOGDEBUG, "Joystick %d Hat %d Down with position %d", joyId, buttonId, m_HatState); + break; + + case SDL_JOYBALLMOTION: + ignore = true; + break; + + case SDL_JOYBUTTONUP: + m_pressTicksButton = 0; + SetButtonActive(false); + CLog::Log(LOGDEBUG, "Joystick %d button %d Up", joyEvent.jbutton.which, m_ButtonId); + + default: + ignore = true; + break; + } +} + +bool CJoystick::GetHat(int &id, int &position,bool consider_repeat) +{ + if (!IsHatActive()) + return false; + position = m_HatState; + id = m_HatId; + if (!consider_repeat) + return true; + + static Uint32 lastPressTicks = 0; + static Uint32 lastTicks = 0; + static Uint32 nowTicks = 0; + + if ((m_HatId>=0) && m_pressTicksHat) + { + // return the id if it's the first press + if (lastPressTicks!=m_pressTicksHat) + { + lastPressTicks = m_pressTicksHat; + return true; + } + nowTicks = SDL_GetTicks(); + if ((nowTicks-m_pressTicksHat)<500) // 500ms delay before we repeat + return false; + if ((nowTicks-lastTicks)<100) // 100ms delay before successive repeats + return false; + + lastTicks = nowTicks; + } + + return true; +} + +bool CJoystick::GetButton(int &id, bool consider_repeat) +{ + if (!IsButtonActive()) + return false; + if (!consider_repeat) + { + id = m_ButtonId; + return true; + } + + static Uint32 lastPressTicks = 0; + static Uint32 lastTicks = 0; + static Uint32 nowTicks = 0; + + if ((m_ButtonId>=0) && m_pressTicksButton) + { + // return the id if it's the first press + if (lastPressTicks!=m_pressTicksButton) + { + lastPressTicks = m_pressTicksButton; + id = m_ButtonId; + return true; + } + nowTicks = SDL_GetTicks(); + if ((nowTicks-m_pressTicksButton)<500) // 500ms delay before we repeat + { + return false; + } + if ((nowTicks-lastTicks)<100) // 100ms delay before successive repeats + { + return false; + } + lastTicks = nowTicks; + } + id = m_ButtonId; + return true; +} + +int CJoystick::GetAxisWithMaxAmount() +{ + static int maxAmount; + static int axis; + axis = 0; + maxAmount = m_SafeRange; + int tempf; + for (int i = 1 ; i<=m_NumAxes ; i++) + { + tempf = abs(m_DefaultAmount[i] - m_Amount[i]); + if (tempf>maxAmount) + { + maxAmount = tempf; + axis = i; + } + } + if (maxAmount==0) + SetAxisActive(false); + else + SetAxisActive(); + return axis; +} + +#endif diff --git a/guilib/common/SDLJoystick.h b/guilib/common/SDLJoystick.h new file mode 100644 index 0000000000..1aa89fecd4 --- /dev/null +++ b/guilib/common/SDLJoystick.h @@ -0,0 +1,78 @@ +#ifndef SDL_JOYSTICK_H +#define SDL_JOYSTICK_H + +#include "../system.h" +#include <vector> +#include <string> + +#ifdef HAS_SDL_JOYSTICK + +#include <SDL/SDL_joystick.h> +#include <SDL/SDL_events.h> + +#define MAX_AXES 64 + +#define JACTIVE_BUTTON 0x00000001 +#define JACTIVE_AXIS 0x00000002 +#define JACTIVE_HAT 0x00000004 +#define JACTIVE_NONE 0x00000000 + +// Class to manage all connected joysticks + +class CJoystick +{ +public: + CJoystick(); + + void Initialize(HWND hwnd); + void Reset(bool axis=false); + void CalibrateAxis(SDL_Joystick *joy); + void ResetAxis(int axisId) { m_Amount[axisId] = 0; } + void Update(); + void Update(SDL_Event& event); + float GetAmount(int axis) + { + if (m_Amount[axis]>0) + return (float)(m_Amount[axis]-m_SafeRange)/(32768.0f-(float)m_SafeRange); + return (float)(m_Amount[axis]+m_SafeRange)/(32768.0f-(float)m_SafeRange); + } + float GetAmount() + { + return GetAmount(m_AxisId); + } + bool GetButton (int& id, bool consider_repeat=true); + bool GetAxis (int &id) { if (!IsAxisActive()) return false; id=m_AxisId; return true; } + bool GetHat (int &id, int &position, bool consider_repeat=true); + std::string GetJoystick() { return (m_JoyId>-1)?m_JoystickNames[m_JoyId]:""; } + int GetAxisWithMaxAmount(); + void SetSafeRange(int val) { m_SafeRange=(val>32767)?32767:val; } + +private: + void SetAxisActive(bool active=true) { m_ActiveFlags = active?(m_ActiveFlags|JACTIVE_AXIS):(m_ActiveFlags&(~JACTIVE_AXIS)); } + void SetButtonActive(bool active=true) { m_ActiveFlags = active?(m_ActiveFlags|JACTIVE_BUTTON):(m_ActiveFlags&(~JACTIVE_BUTTON)); } + void SetHatActive(bool active=true) { m_ActiveFlags = active?(m_ActiveFlags|JACTIVE_HAT):(m_ActiveFlags&(~JACTIVE_HAT)); } + bool IsButtonActive() { return (m_ActiveFlags & JACTIVE_BUTTON) == JACTIVE_BUTTON; } + bool IsAxisActive() { return (m_ActiveFlags & JACTIVE_AXIS) == JACTIVE_AXIS; } + bool IsHatActive() { return (m_ActiveFlags & JACTIVE_HAT) == JACTIVE_HAT; } + + int m_Amount[MAX_AXES]; + int m_DefaultAmount[MAX_AXES]; + int m_AxisId; + int m_ButtonId; + Uint8 m_HatState; + int m_HatId; + int m_JoyId; + int m_NumAxes; + int m_SafeRange; // dead zone + Uint32 m_pressTicksButton; + Uint32 m_pressTicksHat; + Uint8 m_ActiveFlags; + std::vector<SDL_Joystick*> m_Joysticks; + std::vector<std::string> m_JoystickNames; +}; + +extern CJoystick g_Joystick; + +#endif + +#endif diff --git a/guilib/freetype2/freetype239ST.lib b/guilib/freetype2/freetype239ST.lib Binary files differnew file mode 100644 index 0000000000..235a608780 --- /dev/null +++ b/guilib/freetype2/freetype239ST.lib diff --git a/guilib/freetype2/freetype239ST_D.lib b/guilib/freetype2/freetype239ST_D.lib Binary files differnew file mode 100644 index 0000000000..0c198a8432 --- /dev/null +++ b/guilib/freetype2/freetype239ST_D.lib diff --git a/guilib/freetype2/include/freetype/config/ftconfig.h b/guilib/freetype2/include/freetype/config/ftconfig.h new file mode 100644 index 0000000000..3c0b8b1641 --- /dev/null +++ b/guilib/freetype2/include/freetype/config/ftconfig.h @@ -0,0 +1,500 @@ +/***************************************************************************/ +/* */ +/* ftconfig.h */ +/* */ +/* ANSI-specific configuration file (specification only). */ +/* */ +/* Copyright 1996-2001, 2002, 2003, 2004, 2006, 2007, 2008 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This header file contains a number of macro definitions that are used */ + /* by the rest of the engine. Most of the macros here are automatically */ + /* determined at compile time, and you should not need to change it to */ + /* port FreeType, except to compile the library with a non-ANSI */ + /* compiler. */ + /* */ + /* Note however that if some specific modifications are needed, we */ + /* advise you to place a modified copy in your build directory. */ + /* */ + /* The build directory is usually `freetype/builds/<system>', and */ + /* contains system-specific files that are always included first when */ + /* building the library. */ + /* */ + /* This ANSI version should stay in `include/freetype/config'. */ + /* */ + /*************************************************************************/ + + +#ifndef __FTCONFIG_H__ +#define __FTCONFIG_H__ + +#include <ft2build.h> +#include FT_CONFIG_OPTIONS_H +#include FT_CONFIG_STANDARD_LIBRARY_H + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* PLATFORM-SPECIFIC CONFIGURATION MACROS */ + /* */ + /* These macros can be toggled to suit a specific system. The current */ + /* ones are defaults used to compile FreeType in an ANSI C environment */ + /* (16bit compilers are also supported). Copy this file to your own */ + /* `freetype/builds/<system>' directory, and edit it to port the engine. */ + /* */ + /*************************************************************************/ + + + /* There are systems (like the Texas Instruments 'C54x) where a `char' */ + /* has 16 bits. ANSI C says that sizeof(char) is always 1. Since an */ + /* `int' has 16 bits also for this system, sizeof(int) gives 1 which */ + /* is probably unexpected. */ + /* */ + /* `CHAR_BIT' (defined in limits.h) gives the number of bits in a */ + /* `char' type. */ + +#ifndef FT_CHAR_BIT +#define FT_CHAR_BIT CHAR_BIT +#endif + + + /* The size of an `int' type. */ +#if FT_UINT_MAX == 0xFFFFUL +#define FT_SIZEOF_INT (16 / FT_CHAR_BIT) +#elif FT_UINT_MAX == 0xFFFFFFFFUL +#define FT_SIZEOF_INT (32 / FT_CHAR_BIT) +#elif FT_UINT_MAX > 0xFFFFFFFFUL && FT_UINT_MAX == 0xFFFFFFFFFFFFFFFFUL +#define FT_SIZEOF_INT (64 / FT_CHAR_BIT) +#else +#error "Unsupported size of `int' type!" +#endif + + /* The size of a `long' type. A five-byte `long' (as used e.g. on the */ + /* DM642) is recognized but avoided. */ +#if FT_ULONG_MAX == 0xFFFFFFFFUL +#define FT_SIZEOF_LONG (32 / FT_CHAR_BIT) +#elif FT_ULONG_MAX > 0xFFFFFFFFUL && FT_ULONG_MAX == 0xFFFFFFFFFFUL +#define FT_SIZEOF_LONG (32 / FT_CHAR_BIT) +#elif FT_ULONG_MAX > 0xFFFFFFFFUL && FT_ULONG_MAX == 0xFFFFFFFFFFFFFFFFUL +#define FT_SIZEOF_LONG (64 / FT_CHAR_BIT) +#else +#error "Unsupported size of `long' type!" +#endif + + + /* Preferred alignment of data */ +#define FT_ALIGNMENT 8 + + + /* FT_UNUSED is a macro used to indicate that a given parameter is not */ + /* used -- this is only used to get rid of unpleasant compiler warnings */ +#ifndef FT_UNUSED +#define FT_UNUSED( arg ) ( (arg) = (arg) ) +#endif + + + /*************************************************************************/ + /* */ + /* AUTOMATIC CONFIGURATION MACROS */ + /* */ + /* These macros are computed from the ones defined above. Don't touch */ + /* their definition, unless you know precisely what you are doing. No */ + /* porter should need to mess with them. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Mac support */ + /* */ + /* This is the only necessary change, so it is defined here instead */ + /* providing a new configuration file. */ + /* */ +#if ( defined( __APPLE__ ) && !defined( DARWIN_NO_CARBON ) ) || \ + ( defined( __MWERKS__ ) && defined( macintosh ) ) + /* no Carbon frameworks for 64bit 10.4.x */ +#include "AvailabilityMacros.h" +#if defined( __LP64__ ) && \ + ( MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 ) +#define DARWIN_NO_CARBON 1 +#else +#define FT_MACINTOSH 1 +#endif + +#elif defined( __SC__ ) || defined( __MRC__ ) + /* Classic MacOS compilers */ +#include "ConditionalMacros.h" +#if TARGET_OS_MAC +#define FT_MACINTOSH 1 +#endif + +#endif + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* basic_types */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Int16 */ + /* */ + /* <Description> */ + /* A typedef for a 16bit signed integer type. */ + /* */ + typedef signed short FT_Int16; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_UInt16 */ + /* */ + /* <Description> */ + /* A typedef for a 16bit unsigned integer type. */ + /* */ + typedef unsigned short FT_UInt16; + + /* */ + + + /* this #if 0 ... #endif clause is for documentation purposes */ +#if 0 + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Int32 */ + /* */ + /* <Description> */ + /* A typedef for a 32bit signed integer type. The size depends on */ + /* the configuration. */ + /* */ + typedef signed XXX FT_Int32; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_UInt32 */ + /* */ + /* A typedef for a 32bit unsigned integer type. The size depends on */ + /* the configuration. */ + /* */ + typedef unsigned XXX FT_UInt32; + + /* */ + +#endif + +#if FT_SIZEOF_INT == (32 / FT_CHAR_BIT) + + typedef signed int FT_Int32; + typedef unsigned int FT_UInt32; + +#elif FT_SIZEOF_LONG == (32 / FT_CHAR_BIT) + + typedef signed long FT_Int32; + typedef unsigned long FT_UInt32; + +#else +#error "no 32bit type found -- please check your configuration files" +#endif + + + /* look up an integer type that is at least 32 bits */ +#if FT_SIZEOF_INT >= (32 / FT_CHAR_BIT) + + typedef int FT_Fast; + typedef unsigned int FT_UFast; + +#elif FT_SIZEOF_LONG >= (32 / FT_CHAR_BIT) + + typedef long FT_Fast; + typedef unsigned long FT_UFast; + +#endif + + + /* determine whether we have a 64-bit int type for platforms without */ + /* Autoconf */ +#if FT_SIZEOF_LONG == (64 / FT_CHAR_BIT) + + /* FT_LONG64 must be defined if a 64-bit type is available */ +#define FT_LONG64 +#define FT_INT64 long + +#elif defined( _MSC_VER ) && _MSC_VER >= 900 /* Visual C++ (and Intel C++) */ + + /* this compiler provides the __int64 type */ +#define FT_LONG64 +#define FT_INT64 __int64 + +#elif defined( __BORLANDC__ ) /* Borland C++ */ + + /* XXXX: We should probably check the value of __BORLANDC__ in order */ + /* to test the compiler version. */ + + /* this compiler provides the __int64 type */ +#define FT_LONG64 +#define FT_INT64 __int64 + +#elif defined( __WATCOMC__ ) /* Watcom C++ */ + + /* Watcom doesn't provide 64-bit data types */ + +#elif defined( __MWERKS__ ) /* Metrowerks CodeWarrior */ + +#define FT_LONG64 +#define FT_INT64 long long int + +#elif defined( __GNUC__ ) + + /* GCC provides the `long long' type */ +#define FT_LONG64 +#define FT_INT64 long long int + +#endif /* FT_SIZEOF_LONG == (64 / FT_CHAR_BIT) */ + + + /*************************************************************************/ + /* */ + /* A 64-bit data type will create compilation problems if you compile */ + /* in strict ANSI mode. To avoid them, we disable its use if __STDC__ */ + /* is defined. You can however ignore this rule by defining the */ + /* FT_CONFIG_OPTION_FORCE_INT64 configuration macro. */ + /* */ +#if defined( FT_LONG64 ) && !defined( FT_CONFIG_OPTION_FORCE_INT64 ) + +#ifdef __STDC__ + + /* undefine the 64-bit macros in strict ANSI compilation mode */ +#undef FT_LONG64 +#undef FT_INT64 + +#endif /* __STDC__ */ + +#endif /* FT_LONG64 && !FT_CONFIG_OPTION_FORCE_INT64 */ + + +#define FT_BEGIN_STMNT do { +#define FT_END_STMNT } while ( 0 ) +#define FT_DUMMY_STMNT FT_BEGIN_STMNT FT_END_STMNT + + +#ifndef FT_CONFIG_OPTION_NO_ASSEMBLER + /* Provide assembler fragments for performance-critical functions. */ + /* These must be defined `static __inline__' with GCC. */ + +#ifdef __GNUC__ + +#if defined( __arm__ ) && !defined( __thumb__ ) +#define FT_MULFIX_ASSEMBLER FT_MulFix_arm + + /* documentation is in freetype.h */ + + static __inline__ FT_Int32 + FT_MulFix_arm( FT_Int32 a, + FT_Int32 b ) + { + register FT_Int32 t, t2; + + + asm __volatile__ ( + "smull %1, %2, %4, %3\n\t" /* (lo=%1,hi=%2) = a*b */ + "mov %0, %2, asr #31\n\t" /* %0 = (hi >> 31) */ + "add %0, %0, #0x8000\n\t" /* %0 += 0x8000 */ + "adds %1, %1, %0\n\t" /* %1 += %0 */ + "adc %2, %2, #0\n\t" /* %2 += carry */ + "mov %0, %1, lsr #16\n\t" /* %0 = %1 >> 16 */ + "orr %0, %2, lsl #16\n\t" /* %0 |= %2 << 16 */ + : "=r"(a), "=&r"(t2), "=&r"(t) + : "r"(a), "r"(b) ); + return a; + } + +#endif /* __arm__ && !__thumb__ */ + +#if defined( i386 ) +#define FT_MULFIX_ASSEMBLER FT_MulFix_i386 + + /* documentation is in freetype.h */ + + static __inline__ FT_Int32 + FT_MulFix_i386( FT_Int32 a, + FT_Int32 b ) + { + register FT_Int32 result; + + + __asm__ __volatile__ ( + "imul %%edx\n" + "movl %%edx, %%ecx\n" + "sarl $31, %%ecx\n" + "addl $0x8000, %%ecx\n" + "addl %%ecx, %%eax\n" + "adcl $0, %%edx\n" + "shrl $16, %%eax\n" + "shll $16, %%edx\n" + "addl %%edx, %%eax\n" + : "=a"(result), "=d"(b) + : "a"(a), "d"(b) + : "%ecx", "cc" ); + return result; + } + +#endif /* i386 */ + +#endif /* __GNUC__ */ + +#endif /* !FT_CONFIG_OPTION_NO_ASSEMBLER */ + + +#ifdef FT_CONFIG_OPTION_INLINE_MULFIX +#ifdef FT_MULFIX_ASSEMBLER +#define FT_MULFIX_INLINED FT_MULFIX_ASSEMBLER +#endif +#endif + + +#ifdef FT_MAKE_OPTION_SINGLE_OBJECT + +#define FT_LOCAL( x ) static x +#define FT_LOCAL_DEF( x ) static x + +#else + +#ifdef __cplusplus +#define FT_LOCAL( x ) extern "C" x +#define FT_LOCAL_DEF( x ) extern "C" x +#else +#define FT_LOCAL( x ) extern x +#define FT_LOCAL_DEF( x ) x +#endif + +#endif /* FT_MAKE_OPTION_SINGLE_OBJECT */ + + +#ifndef FT_BASE + +#ifdef __cplusplus +#define FT_BASE( x ) extern "C" x +#else +#define FT_BASE( x ) extern x +#endif + +#endif /* !FT_BASE */ + + +#ifndef FT_BASE_DEF + +#ifdef __cplusplus +#define FT_BASE_DEF( x ) x +#else +#define FT_BASE_DEF( x ) x +#endif + +#endif /* !FT_BASE_DEF */ + + +#ifndef FT_EXPORT + +#ifdef __cplusplus +#define FT_EXPORT( x ) extern "C" x +#else +#define FT_EXPORT( x ) extern x +#endif + +#endif /* !FT_EXPORT */ + + +#ifndef FT_EXPORT_DEF + +#ifdef __cplusplus +#define FT_EXPORT_DEF( x ) extern "C" x +#else +#define FT_EXPORT_DEF( x ) extern x +#endif + +#endif /* !FT_EXPORT_DEF */ + + +#ifndef FT_EXPORT_VAR + +#ifdef __cplusplus +#define FT_EXPORT_VAR( x ) extern "C" x +#else +#define FT_EXPORT_VAR( x ) extern x +#endif + +#endif /* !FT_EXPORT_VAR */ + + /* The following macros are needed to compile the library with a */ + /* C++ compiler and with 16bit compilers. */ + /* */ + + /* This is special. Within C++, you must specify `extern "C"' for */ + /* functions which are used via function pointers, and you also */ + /* must do that for structures which contain function pointers to */ + /* assure C linkage -- it's not possible to have (local) anonymous */ + /* functions which are accessed by (global) function pointers. */ + /* */ + /* */ + /* FT_CALLBACK_DEF is used to _define_ a callback function. */ + /* */ + /* FT_CALLBACK_TABLE is used to _declare_ a constant variable that */ + /* contains pointers to callback functions. */ + /* */ + /* FT_CALLBACK_TABLE_DEF is used to _define_ a constant variable */ + /* that contains pointers to callback functions. */ + /* */ + /* */ + /* Some 16bit compilers have to redefine these macros to insert */ + /* the infamous `_cdecl' or `__fastcall' declarations. */ + /* */ +#ifndef FT_CALLBACK_DEF +#ifdef __cplusplus +#define FT_CALLBACK_DEF( x ) extern "C" x +#else +#define FT_CALLBACK_DEF( x ) static x +#endif +#endif /* FT_CALLBACK_DEF */ + +#ifndef FT_CALLBACK_TABLE +#ifdef __cplusplus +#define FT_CALLBACK_TABLE extern "C" +#define FT_CALLBACK_TABLE_DEF extern "C" +#else +#define FT_CALLBACK_TABLE extern +#define FT_CALLBACK_TABLE_DEF /* nothing */ +#endif +#endif /* FT_CALLBACK_TABLE */ + + +FT_END_HEADER + + +#endif /* __FTCONFIG_H__ */ + + +/* END */ diff --git a/guilib/freetype2/include/freetype/config/ftheader.h b/guilib/freetype2/include/freetype/config/ftheader.h new file mode 100644 index 0000000000..b63945dcbd --- /dev/null +++ b/guilib/freetype2/include/freetype/config/ftheader.h @@ -0,0 +1,780 @@ +/***************************************************************************/ +/* */ +/* ftheader.h */ +/* */ +/* Build macros of the FreeType 2 library. */ +/* */ +/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#ifndef __FT_HEADER_H__ +#define __FT_HEADER_H__ + + + /*@***********************************************************************/ + /* */ + /* <Macro> */ + /* FT_BEGIN_HEADER */ + /* */ + /* <Description> */ + /* This macro is used in association with @FT_END_HEADER in header */ + /* files to ensure that the declarations within are properly */ + /* encapsulated in an `extern "C" { .. }' block when included from a */ + /* C++ compiler. */ + /* */ +#ifdef __cplusplus +#define FT_BEGIN_HEADER extern "C" { +#else +#define FT_BEGIN_HEADER /* nothing */ +#endif + + + /*@***********************************************************************/ + /* */ + /* <Macro> */ + /* FT_END_HEADER */ + /* */ + /* <Description> */ + /* This macro is used in association with @FT_BEGIN_HEADER in header */ + /* files to ensure that the declarations within are properly */ + /* encapsulated in an `extern "C" { .. }' block when included from a */ + /* C++ compiler. */ + /* */ +#ifdef __cplusplus +#define FT_END_HEADER } +#else +#define FT_END_HEADER /* nothing */ +#endif + + + /*************************************************************************/ + /* */ + /* Aliases for the FreeType 2 public and configuration files. */ + /* */ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* <Section> */ + /* header_file_macros */ + /* */ + /* <Title> */ + /* Header File Macros */ + /* */ + /* <Abstract> */ + /* Macro definitions used to #include specific header files. */ + /* */ + /* <Description> */ + /* The following macros are defined to the name of specific */ + /* FreeType~2 header files. They can be used directly in #include */ + /* statements as in: */ + /* */ + /* { */ + /* #include FT_FREETYPE_H */ + /* #include FT_MULTIPLE_MASTERS_H */ + /* #include FT_GLYPH_H */ + /* } */ + /* */ + /* There are several reasons why we are now using macros to name */ + /* public header files. The first one is that such macros are not */ + /* limited to the infamous 8.3~naming rule required by DOS (and */ + /* `FT_MULTIPLE_MASTERS_H' is a lot more meaningful than `ftmm.h'). */ + /* */ + /* The second reason is that it allows for more flexibility in the */ + /* way FreeType~2 is installed on a given system. */ + /* */ + /*************************************************************************/ + + + /* configuration files */ + + /************************************************************************* + * + * @macro: + * FT_CONFIG_CONFIG_H + * + * @description: + * A macro used in #include statements to name the file containing + * FreeType~2 configuration data. + * + */ +#ifndef FT_CONFIG_CONFIG_H +#define FT_CONFIG_CONFIG_H <freetype/config/ftconfig.h> +#endif + + + /************************************************************************* + * + * @macro: + * FT_CONFIG_STANDARD_LIBRARY_H + * + * @description: + * A macro used in #include statements to name the file containing + * FreeType~2 interface to the standard C library functions. + * + */ +#ifndef FT_CONFIG_STANDARD_LIBRARY_H +#define FT_CONFIG_STANDARD_LIBRARY_H <freetype/config/ftstdlib.h> +#endif + + + /************************************************************************* + * + * @macro: + * FT_CONFIG_OPTIONS_H + * + * @description: + * A macro used in #include statements to name the file containing + * FreeType~2 project-specific configuration options. + * + */ +#ifndef FT_CONFIG_OPTIONS_H +#define FT_CONFIG_OPTIONS_H <freetype/config/ftoption.h> +#endif + + + /************************************************************************* + * + * @macro: + * FT_CONFIG_MODULES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * list of FreeType~2 modules that are statically linked to new library + * instances in @FT_Init_FreeType. + * + */ +#ifndef FT_CONFIG_MODULES_H +#define FT_CONFIG_MODULES_H <freetype/config/ftmodule.h> +#endif + + /* */ + + /* public headers */ + + /************************************************************************* + * + * @macro: + * FT_FREETYPE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * base FreeType~2 API. + * + */ +#define FT_FREETYPE_H <freetype/freetype.h> + + + /************************************************************************* + * + * @macro: + * FT_ERRORS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * list of FreeType~2 error codes (and messages). + * + * It is included by @FT_FREETYPE_H. + * + */ +#define FT_ERRORS_H <freetype/fterrors.h> + + + /************************************************************************* + * + * @macro: + * FT_MODULE_ERRORS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * list of FreeType~2 module error offsets (and messages). + * + */ +#define FT_MODULE_ERRORS_H <freetype/ftmoderr.h> + + + /************************************************************************* + * + * @macro: + * FT_SYSTEM_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 interface to low-level operations (i.e., memory management + * and stream i/o). + * + * It is included by @FT_FREETYPE_H. + * + */ +#define FT_SYSTEM_H <freetype/ftsystem.h> + + + /************************************************************************* + * + * @macro: + * FT_IMAGE_H + * + * @description: + * A macro used in #include statements to name the file containing type + * definitions related to glyph images (i.e., bitmaps, outlines, + * scan-converter parameters). + * + * It is included by @FT_FREETYPE_H. + * + */ +#define FT_IMAGE_H <freetype/ftimage.h> + + + /************************************************************************* + * + * @macro: + * FT_TYPES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * basic data types defined by FreeType~2. + * + * It is included by @FT_FREETYPE_H. + * + */ +#define FT_TYPES_H <freetype/fttypes.h> + + + /************************************************************************* + * + * @macro: + * FT_LIST_H + * + * @description: + * A macro used in #include statements to name the file containing the + * list management API of FreeType~2. + * + * (Most applications will never need to include this file.) + * + */ +#define FT_LIST_H <freetype/ftlist.h> + + + /************************************************************************* + * + * @macro: + * FT_OUTLINE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * scalable outline management API of FreeType~2. + * + */ +#define FT_OUTLINE_H <freetype/ftoutln.h> + + + /************************************************************************* + * + * @macro: + * FT_SIZES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * API which manages multiple @FT_Size objects per face. + * + */ +#define FT_SIZES_H <freetype/ftsizes.h> + + + /************************************************************************* + * + * @macro: + * FT_MODULE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * module management API of FreeType~2. + * + */ +#define FT_MODULE_H <freetype/ftmodapi.h> + + + /************************************************************************* + * + * @macro: + * FT_RENDER_H + * + * @description: + * A macro used in #include statements to name the file containing the + * renderer module management API of FreeType~2. + * + */ +#define FT_RENDER_H <freetype/ftrender.h> + + + /************************************************************************* + * + * @macro: + * FT_TYPE1_TABLES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * types and API specific to the Type~1 format. + * + */ +#define FT_TYPE1_TABLES_H <freetype/t1tables.h> + + + /************************************************************************* + * + * @macro: + * FT_TRUETYPE_IDS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * enumeration values which identify name strings, languages, encodings, + * etc. This file really contains a _large_ set of constant macro + * definitions, taken from the TrueType and OpenType specifications. + * + */ +#define FT_TRUETYPE_IDS_H <freetype/ttnameid.h> + + + /************************************************************************* + * + * @macro: + * FT_TRUETYPE_TABLES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * types and API specific to the TrueType (as well as OpenType) format. + * + */ +#define FT_TRUETYPE_TABLES_H <freetype/tttables.h> + + + /************************************************************************* + * + * @macro: + * FT_TRUETYPE_TAGS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * definitions of TrueType four-byte `tags' which identify blocks in + * SFNT-based font formats (i.e., TrueType and OpenType). + * + */ +#define FT_TRUETYPE_TAGS_H <freetype/tttags.h> + + + /************************************************************************* + * + * @macro: + * FT_BDF_H + * + * @description: + * A macro used in #include statements to name the file containing the + * definitions of an API which accesses BDF-specific strings from a + * face. + * + */ +#define FT_BDF_H <freetype/ftbdf.h> + + + /************************************************************************* + * + * @macro: + * FT_CID_H + * + * @description: + * A macro used in #include statements to name the file containing the + * definitions of an API which access CID font information from a + * face. + * + */ +#define FT_CID_H <freetype/ftcid.h> + + + /************************************************************************* + * + * @macro: + * FT_GZIP_H + * + * @description: + * A macro used in #include statements to name the file containing the + * definitions of an API which supports gzip-compressed files. + * + */ +#define FT_GZIP_H <freetype/ftgzip.h> + + + /************************************************************************* + * + * @macro: + * FT_LZW_H + * + * @description: + * A macro used in #include statements to name the file containing the + * definitions of an API which supports LZW-compressed files. + * + */ +#define FT_LZW_H <freetype/ftlzw.h> + + + /************************************************************************* + * + * @macro: + * FT_WINFONTS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * definitions of an API which supports Windows FNT files. + * + */ +#define FT_WINFONTS_H <freetype/ftwinfnt.h> + + + /************************************************************************* + * + * @macro: + * FT_GLYPH_H + * + * @description: + * A macro used in #include statements to name the file containing the + * API of the optional glyph management component. + * + */ +#define FT_GLYPH_H <freetype/ftglyph.h> + + + /************************************************************************* + * + * @macro: + * FT_BITMAP_H + * + * @description: + * A macro used in #include statements to name the file containing the + * API of the optional bitmap conversion component. + * + */ +#define FT_BITMAP_H <freetype/ftbitmap.h> + + + /************************************************************************* + * + * @macro: + * FT_BBOX_H + * + * @description: + * A macro used in #include statements to name the file containing the + * API of the optional exact bounding box computation routines. + * + */ +#define FT_BBOX_H <freetype/ftbbox.h> + + + /************************************************************************* + * + * @macro: + * FT_CACHE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * API of the optional FreeType~2 cache sub-system. + * + */ +#define FT_CACHE_H <freetype/ftcache.h> + + + /************************************************************************* + * + * @macro: + * FT_CACHE_IMAGE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * `glyph image' API of the FreeType~2 cache sub-system. + * + * It is used to define a cache for @FT_Glyph elements. You can also + * use the API defined in @FT_CACHE_SMALL_BITMAPS_H if you only need to + * store small glyph bitmaps, as it will use less memory. + * + * This macro is deprecated. Simply include @FT_CACHE_H to have all + * glyph image-related cache declarations. + * + */ +#define FT_CACHE_IMAGE_H FT_CACHE_H + + + /************************************************************************* + * + * @macro: + * FT_CACHE_SMALL_BITMAPS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * `small bitmaps' API of the FreeType~2 cache sub-system. + * + * It is used to define a cache for small glyph bitmaps in a relatively + * memory-efficient way. You can also use the API defined in + * @FT_CACHE_IMAGE_H if you want to cache arbitrary glyph images, + * including scalable outlines. + * + * This macro is deprecated. Simply include @FT_CACHE_H to have all + * small bitmaps-related cache declarations. + * + */ +#define FT_CACHE_SMALL_BITMAPS_H FT_CACHE_H + + + /************************************************************************* + * + * @macro: + * FT_CACHE_CHARMAP_H + * + * @description: + * A macro used in #include statements to name the file containing the + * `charmap' API of the FreeType~2 cache sub-system. + * + * This macro is deprecated. Simply include @FT_CACHE_H to have all + * charmap-based cache declarations. + * + */ +#define FT_CACHE_CHARMAP_H FT_CACHE_H + + + /************************************************************************* + * + * @macro: + * FT_MAC_H + * + * @description: + * A macro used in #include statements to name the file containing the + * Macintosh-specific FreeType~2 API. The latter is used to access + * fonts embedded in resource forks. + * + * This header file must be explicitly included by client applications + * compiled on the Mac (note that the base API still works though). + * + */ +#define FT_MAC_H <freetype/ftmac.h> + + + /************************************************************************* + * + * @macro: + * FT_MULTIPLE_MASTERS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * optional multiple-masters management API of FreeType~2. + * + */ +#define FT_MULTIPLE_MASTERS_H <freetype/ftmm.h> + + + /************************************************************************* + * + * @macro: + * FT_SFNT_NAMES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * optional FreeType~2 API which accesses embedded `name' strings in + * SFNT-based font formats (i.e., TrueType and OpenType). + * + */ +#define FT_SFNT_NAMES_H <freetype/ftsnames.h> + + + /************************************************************************* + * + * @macro: + * FT_OPENTYPE_VALIDATE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * optional FreeType~2 API which validates OpenType tables (BASE, GDEF, + * GPOS, GSUB, JSTF). + * + */ +#define FT_OPENTYPE_VALIDATE_H <freetype/ftotval.h> + + + /************************************************************************* + * + * @macro: + * FT_GX_VALIDATE_H + * + * @description: + * A macro used in #include statements to name the file containing the + * optional FreeType~2 API which validates TrueTypeGX/AAT tables (feat, + * mort, morx, bsln, just, kern, opbd, trak, prop). + * + */ +#define FT_GX_VALIDATE_H <freetype/ftgxval.h> + + + /************************************************************************* + * + * @macro: + * FT_PFR_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which accesses PFR-specific data. + * + */ +#define FT_PFR_H <freetype/ftpfr.h> + + + /************************************************************************* + * + * @macro: + * FT_STROKER_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which provides functions to stroke outline paths. + */ +#define FT_STROKER_H <freetype/ftstroke.h> + + + /************************************************************************* + * + * @macro: + * FT_SYNTHESIS_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which performs artificial obliquing and emboldening. + */ +#define FT_SYNTHESIS_H <freetype/ftsynth.h> + + + /************************************************************************* + * + * @macro: + * FT_XFREE86_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which provides functions specific to the XFree86 and + * X.Org X11 servers. + */ +#define FT_XFREE86_H <freetype/ftxf86.h> + + + /************************************************************************* + * + * @macro: + * FT_TRIGONOMETRY_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which performs trigonometric computations (e.g., + * cosines and arc tangents). + */ +#define FT_TRIGONOMETRY_H <freetype/fttrigon.h> + + + /************************************************************************* + * + * @macro: + * FT_LCD_FILTER_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which performs color filtering for subpixel rendering. + */ +#define FT_LCD_FILTER_H <freetype/ftlcdfil.h> + + + /************************************************************************* + * + * @macro: + * FT_UNPATENTED_HINTING_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which performs color filtering for subpixel rendering. + */ +#define FT_UNPATENTED_HINTING_H <freetype/ttunpat.h> + + + /************************************************************************* + * + * @macro: + * FT_INCREMENTAL_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which performs color filtering for subpixel rendering. + */ +#define FT_INCREMENTAL_H <freetype/ftincrem.h> + + + /************************************************************************* + * + * @macro: + * FT_GASP_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which returns entries from the TrueType GASP table. + */ +#define FT_GASP_H <freetype/ftgasp.h> + + + /************************************************************************* + * + * @macro: + * FT_ADVANCES_H + * + * @description: + * A macro used in #include statements to name the file containing the + * FreeType~2 API which returns individual and ranged glyph advances. + */ +#define FT_ADVANCES_H <freetype/ftadvanc.h> + + + /* */ + +#define FT_ERROR_DEFINITIONS_H <freetype/fterrdef.h> + + + /* The internals of the cache sub-system are no longer exposed. We */ + /* default to FT_CACHE_H at the moment just in case, but we know of */ + /* no rogue client that uses them. */ + /* */ +#define FT_CACHE_MANAGER_H <freetype/ftcache.h> +#define FT_CACHE_INTERNAL_MRU_H <freetype/ftcache.h> +#define FT_CACHE_INTERNAL_MANAGER_H <freetype/ftcache.h> +#define FT_CACHE_INTERNAL_CACHE_H <freetype/ftcache.h> +#define FT_CACHE_INTERNAL_GLYPH_H <freetype/ftcache.h> +#define FT_CACHE_INTERNAL_IMAGE_H <freetype/ftcache.h> +#define FT_CACHE_INTERNAL_SBITS_H <freetype/ftcache.h> + + +#define FT_INCREMENTAL_H <freetype/ftincrem.h> + +#define FT_TRUETYPE_UNPATENTED_H <freetype/ttunpat.h> + + + /* + * Include internal headers definitions from <freetype/internal/...> + * only when building the library. + */ +#ifdef FT2_BUILD_LIBRARY +#define FT_INTERNAL_INTERNAL_H <freetype/internal/internal.h> +#include FT_INTERNAL_INTERNAL_H +#endif /* FT2_BUILD_LIBRARY */ + + +#endif /* __FT2_BUILD_H__ */ + + +/* END */ diff --git a/guilib/freetype2/include/freetype/config/ftoption.h b/guilib/freetype2/include/freetype/config/ftoption.h new file mode 100644 index 0000000000..597a2bb014 --- /dev/null +++ b/guilib/freetype2/include/freetype/config/ftoption.h @@ -0,0 +1,693 @@ +/***************************************************************************/ +/* */ +/* ftoption.h */ +/* */ +/* User-selectable configuration macros (specification only). */ +/* */ +/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTOPTION_H__ +#define __FTOPTION_H__ + + +#include <ft2build.h> + + +FT_BEGIN_HEADER + + /*************************************************************************/ + /* */ + /* USER-SELECTABLE CONFIGURATION MACROS */ + /* */ + /* This file contains the default configuration macro definitions for */ + /* a standard build of the FreeType library. There are three ways to */ + /* use this file to build project-specific versions of the library: */ + /* */ + /* - You can modify this file by hand, but this is not recommended in */ + /* cases where you would like to build several versions of the */ + /* library from a single source directory. */ + /* */ + /* - You can put a copy of this file in your build directory, more */ + /* precisely in `$BUILD/freetype/config/ftoption.h', where `$BUILD' */ + /* is the name of a directory that is included _before_ the FreeType */ + /* include path during compilation. */ + /* */ + /* The default FreeType Makefiles and Jamfiles use the build */ + /* directory `builds/<system>' by default, but you can easily change */ + /* that for your own projects. */ + /* */ + /* - Copy the file <ft2build.h> to `$BUILD/ft2build.h' and modify it */ + /* slightly to pre-define the macro FT_CONFIG_OPTIONS_H used to */ + /* locate this file during the build. For example, */ + /* */ + /* #define FT_CONFIG_OPTIONS_H <myftoptions.h> */ + /* #include <freetype/config/ftheader.h> */ + /* */ + /* will use `$BUILD/myftoptions.h' instead of this file for macro */ + /* definitions. */ + /* */ + /* Note also that you can similarly pre-define the macro */ + /* FT_CONFIG_MODULES_H used to locate the file listing of the modules */ + /* that are statically linked to the library at compile time. By */ + /* default, this file is <freetype/config/ftmodule.h>. */ + /* */ + /* We highly recommend using the third method whenever possible. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** G E N E R A L F R E E T Y P E 2 C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Uncomment the line below if you want to activate sub-pixel rendering */ + /* (a.k.a. LCD rendering, or ClearType) in this build of the library. */ + /* */ + /* Note that this feature is covered by several Microsoft patents */ + /* and should not be activated in any default build of the library. */ + /* */ + /* This macro has no impact on the FreeType API, only on its */ + /* _implementation_. For example, using FT_RENDER_MODE_LCD when calling */ + /* FT_Render_Glyph still generates a bitmap that is 3 times larger than */ + /* the original size; the difference will be that each triplet of */ + /* subpixels has R=G=B. */ + /* */ + /* This is done to allow FreeType clients to run unmodified, forcing */ + /* them to display normal gray-level anti-aliased glyphs. */ + /* */ +/* #define FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ + + + /*************************************************************************/ + /* */ + /* Many compilers provide a non-ANSI 64-bit data type that can be used */ + /* by FreeType to speed up some computations. However, this will create */ + /* some problems when compiling the library in strict ANSI mode. */ + /* */ + /* For this reason, the use of 64-bit integers is normally disabled when */ + /* the __STDC__ macro is defined. You can however disable this by */ + /* defining the macro FT_CONFIG_OPTION_FORCE_INT64 here. */ + /* */ + /* For most compilers, this will only create compilation warnings when */ + /* building the library. */ + /* */ + /* ObNote: The compiler-specific 64-bit integers are detected in the */ + /* file `ftconfig.h' either statically or through the */ + /* `configure' script on supported platforms. */ + /* */ +#undef FT_CONFIG_OPTION_FORCE_INT64 + + + /*************************************************************************/ + /* */ + /* If this macro is defined, do not try to use an assembler version of */ + /* performance-critical functions (e.g. FT_MulFix). You should only do */ + /* that to verify that the assembler function works properly, or to */ + /* execute benchmark tests of the various implementations. */ +/* #define FT_CONFIG_OPTION_NO_ASSEMBLER */ + + + /*************************************************************************/ + /* */ + /* If this macro is defined, try to use an inlined assembler version of */ + /* the `FT_MulFix' function, which is a `hotspot' when loading and */ + /* hinting glyphs, and which should be executed as fast as possible. */ + /* */ + /* Note that if your compiler or CPU is not supported, this will default */ + /* to the standard and portable implementation found in `ftcalc.c'. */ + /* */ +#define FT_CONFIG_OPTION_INLINE_MULFIX + + + /*************************************************************************/ + /* */ + /* LZW-compressed file support. */ + /* */ + /* FreeType now handles font files that have been compressed with the */ + /* `compress' program. This is mostly used to parse many of the PCF */ + /* files that come with various X11 distributions. The implementation */ + /* uses NetBSD's `zopen' to partially uncompress the file on the fly */ + /* (see src/lzw/ftgzip.c). */ + /* */ + /* Define this macro if you want to enable this `feature'. */ + /* */ +#define FT_CONFIG_OPTION_USE_LZW + + + /*************************************************************************/ + /* */ + /* Gzip-compressed file support. */ + /* */ + /* FreeType now handles font files that have been compressed with the */ + /* `gzip' program. This is mostly used to parse many of the PCF files */ + /* that come with XFree86. The implementation uses `zlib' to */ + /* partially uncompress the file on the fly (see src/gzip/ftgzip.c). */ + /* */ + /* Define this macro if you want to enable this `feature'. See also */ + /* the macro FT_CONFIG_OPTION_SYSTEM_ZLIB below. */ + /* */ +#define FT_CONFIG_OPTION_USE_ZLIB + + + /*************************************************************************/ + /* */ + /* ZLib library selection */ + /* */ + /* This macro is only used when FT_CONFIG_OPTION_USE_ZLIB is defined. */ + /* It allows FreeType's `ftgzip' component to link to the system's */ + /* installation of the ZLib library. This is useful on systems like */ + /* Unix or VMS where it generally is already available. */ + /* */ + /* If you let it undefined, the component will use its own copy */ + /* of the zlib sources instead. These have been modified to be */ + /* included directly within the component and *not* export external */ + /* function names. This allows you to link any program with FreeType */ + /* _and_ ZLib without linking conflicts. */ + /* */ + /* Do not #undef this macro here since the build system might define */ + /* it for certain configurations only. */ + /* */ +/* #define FT_CONFIG_OPTION_SYSTEM_ZLIB */ + + + /*************************************************************************/ + /* */ + /* DLL export compilation */ + /* */ + /* When compiling FreeType as a DLL, some systems/compilers need a */ + /* special keyword in front OR after the return type of function */ + /* declarations. */ + /* */ + /* Two macros are used within the FreeType source code to define */ + /* exported library functions: FT_EXPORT and FT_EXPORT_DEF. */ + /* */ + /* FT_EXPORT( return_type ) */ + /* */ + /* is used in a function declaration, as in */ + /* */ + /* FT_EXPORT( FT_Error ) */ + /* FT_Init_FreeType( FT_Library* alibrary ); */ + /* */ + /* */ + /* FT_EXPORT_DEF( return_type ) */ + /* */ + /* is used in a function definition, as in */ + /* */ + /* FT_EXPORT_DEF( FT_Error ) */ + /* FT_Init_FreeType( FT_Library* alibrary ) */ + /* { */ + /* ... some code ... */ + /* return FT_Err_Ok; */ + /* } */ + /* */ + /* You can provide your own implementation of FT_EXPORT and */ + /* FT_EXPORT_DEF here if you want. If you leave them undefined, they */ + /* will be later automatically defined as `extern return_type' to */ + /* allow normal compilation. */ + /* */ + /* Do not #undef these macros here since the build system might define */ + /* them for certain configurations only. */ + /* */ +/* #define FT_EXPORT(x) extern x */ +/* #define FT_EXPORT_DEF(x) x */ + + + /*************************************************************************/ + /* */ + /* Glyph Postscript Names handling */ + /* */ + /* By default, FreeType 2 is compiled with the `psnames' module. This */ + /* module is in charge of converting a glyph name string into a */ + /* Unicode value, or return a Macintosh standard glyph name for the */ + /* use with the TrueType `post' table. */ + /* */ + /* Undefine this macro if you do not want `psnames' compiled in your */ + /* build of FreeType. This has the following effects: */ + /* */ + /* - The TrueType driver will provide its own set of glyph names, */ + /* if you build it to support postscript names in the TrueType */ + /* `post' table. */ + /* */ + /* - The Type 1 driver will not be able to synthesize a Unicode */ + /* charmap out of the glyphs found in the fonts. */ + /* */ + /* You would normally undefine this configuration macro when building */ + /* a version of FreeType that doesn't contain a Type 1 or CFF driver. */ + /* */ +#define FT_CONFIG_OPTION_POSTSCRIPT_NAMES + + + /*************************************************************************/ + /* */ + /* Postscript Names to Unicode Values support */ + /* */ + /* By default, FreeType 2 is built with the `PSNames' module compiled */ + /* in. Among other things, the module is used to convert a glyph name */ + /* into a Unicode value. This is especially useful in order to */ + /* synthesize on the fly a Unicode charmap from the CFF/Type 1 driver */ + /* through a big table named the `Adobe Glyph List' (AGL). */ + /* */ + /* Undefine this macro if you do not want the Adobe Glyph List */ + /* compiled in your `PSNames' module. The Type 1 driver will not be */ + /* able to synthesize a Unicode charmap out of the glyphs found in the */ + /* fonts. */ + /* */ +#define FT_CONFIG_OPTION_ADOBE_GLYPH_LIST + + + /*************************************************************************/ + /* */ + /* Support for Mac fonts */ + /* */ + /* Define this macro if you want support for outline fonts in Mac */ + /* format (mac dfont, mac resource, macbinary containing a mac */ + /* resource) on non-Mac platforms. */ + /* */ + /* Note that the `FOND' resource isn't checked. */ + /* */ +#define FT_CONFIG_OPTION_MAC_FONTS + + + /*************************************************************************/ + /* */ + /* Guessing methods to access embedded resource forks */ + /* */ + /* Enable extra Mac fonts support on non-Mac platforms (e.g. */ + /* GNU/Linux). */ + /* */ + /* Resource forks which include fonts data are stored sometimes in */ + /* locations which users or developers don't expected. In some cases, */ + /* resource forks start with some offset from the head of a file. In */ + /* other cases, the actual resource fork is stored in file different */ + /* from what the user specifies. If this option is activated, */ + /* FreeType tries to guess whether such offsets or different file */ + /* names must be used. */ + /* */ + /* Note that normal, direct access of resource forks is controlled via */ + /* the FT_CONFIG_OPTION_MAC_FONTS option. */ + /* */ +#ifdef FT_CONFIG_OPTION_MAC_FONTS +#define FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK +#endif + + + /*************************************************************************/ + /* */ + /* Allow the use of FT_Incremental_Interface to load typefaces that */ + /* contain no glyph data, but supply it via a callback function. */ + /* This allows FreeType to be used with the PostScript language, using */ + /* the GhostScript interpreter. */ + /* */ +/* #define FT_CONFIG_OPTION_INCREMENTAL */ + + + /*************************************************************************/ + /* */ + /* The size in bytes of the render pool used by the scan-line converter */ + /* to do all of its work. */ + /* */ + /* This must be greater than 4KByte if you use FreeType to rasterize */ + /* glyphs; otherwise, you may set it to zero to avoid unnecessary */ + /* allocation of the render pool. */ + /* */ +#define FT_RENDER_POOL_SIZE 16384L + + + /*************************************************************************/ + /* */ + /* FT_MAX_MODULES */ + /* */ + /* The maximum number of modules that can be registered in a single */ + /* FreeType library object. 32 is the default. */ + /* */ +#define FT_MAX_MODULES 32 + + + /*************************************************************************/ + /* */ + /* Debug level */ + /* */ + /* FreeType can be compiled in debug or trace mode. In debug mode, */ + /* errors are reported through the `ftdebug' component. In trace */ + /* mode, additional messages are sent to the standard output during */ + /* execution. */ + /* */ + /* Define FT_DEBUG_LEVEL_ERROR to build the library in debug mode. */ + /* Define FT_DEBUG_LEVEL_TRACE to build it in trace mode. */ + /* */ + /* Don't define any of these macros to compile in `release' mode! */ + /* */ + /* Do not #undef these macros here since the build system might define */ + /* them for certain configurations only. */ + /* */ +/* #define FT_DEBUG_LEVEL_ERROR */ +/* #define FT_DEBUG_LEVEL_TRACE */ + + + /*************************************************************************/ + /* */ + /* Memory Debugging */ + /* */ + /* FreeType now comes with an integrated memory debugger that is */ + /* capable of detecting simple errors like memory leaks or double */ + /* deletes. To compile it within your build of the library, you */ + /* should define FT_DEBUG_MEMORY here. */ + /* */ + /* Note that the memory debugger is only activated at runtime when */ + /* when the _environment_ variable `FT2_DEBUG_MEMORY' is defined also! */ + /* */ + /* Do not #undef this macro here since the build system might define */ + /* it for certain configurations only. */ + /* */ +/* #define FT_DEBUG_MEMORY */ + + + /*************************************************************************/ + /* */ + /* Module errors */ + /* */ + /* If this macro is set (which is _not_ the default), the higher byte */ + /* of an error code gives the module in which the error has occurred, */ + /* while the lower byte is the real error code. */ + /* */ + /* Setting this macro makes sense for debugging purposes only, since */ + /* it would break source compatibility of certain programs that use */ + /* FreeType 2. */ + /* */ + /* More details can be found in the files ftmoderr.h and fterrors.h. */ + /* */ +#undef FT_CONFIG_OPTION_USE_MODULE_ERRORS + + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** S F N T D R I V E R C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_EMBEDDED_BITMAPS if you want to support */ + /* embedded bitmaps in all formats using the SFNT module (namely */ + /* TrueType & OpenType). */ + /* */ +#define TT_CONFIG_OPTION_EMBEDDED_BITMAPS + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_POSTSCRIPT_NAMES if you want to be able to */ + /* load and enumerate the glyph Postscript names in a TrueType or */ + /* OpenType file. */ + /* */ + /* Note that when you do not compile the `PSNames' module by undefining */ + /* the above FT_CONFIG_OPTION_POSTSCRIPT_NAMES, the `sfnt' module will */ + /* contain additional code used to read the PS Names table from a font. */ + /* */ + /* (By default, the module uses `PSNames' to extract glyph names.) */ + /* */ +#define TT_CONFIG_OPTION_POSTSCRIPT_NAMES + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_SFNT_NAMES if your applications need to */ + /* access the internal name table in a SFNT-based format like TrueType */ + /* or OpenType. The name table contains various strings used to */ + /* describe the font, like family name, copyright, version, etc. It */ + /* does not contain any glyph name though. */ + /* */ + /* Accessing SFNT names is done through the functions declared in */ + /* `freetype/ftnames.h'. */ + /* */ +#define TT_CONFIG_OPTION_SFNT_NAMES + + + /*************************************************************************/ + /* */ + /* TrueType CMap support */ + /* */ + /* Here you can fine-tune which TrueType CMap table format shall be */ + /* supported. */ +#define TT_CONFIG_CMAP_FORMAT_0 +#define TT_CONFIG_CMAP_FORMAT_2 +#define TT_CONFIG_CMAP_FORMAT_4 +#define TT_CONFIG_CMAP_FORMAT_6 +#define TT_CONFIG_CMAP_FORMAT_8 +#define TT_CONFIG_CMAP_FORMAT_10 +#define TT_CONFIG_CMAP_FORMAT_12 +#define TT_CONFIG_CMAP_FORMAT_14 + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** T R U E T Y P E D R I V E R C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_BYTECODE_INTERPRETER if you want to compile */ + /* a bytecode interpreter in the TrueType driver. Note that there are */ + /* important patent issues related to the use of the interpreter. */ + /* */ + /* By undefining this, you will only compile the code necessary to load */ + /* TrueType glyphs without hinting. */ + /* */ + /* Do not #undef this macro here, since the build system might */ + /* define it for certain configurations only. */ + /* */ +/* #define TT_CONFIG_OPTION_BYTECODE_INTERPRETER */ + + + /*************************************************************************/ + /* */ + /* If you define TT_CONFIG_OPTION_UNPATENTED_HINTING, a special version */ + /* of the TrueType bytecode interpreter is used that doesn't implement */ + /* any of the patented opcodes and algorithms. Note that the */ + /* TT_CONFIG_OPTION_UNPATENTED_HINTING macro is *ignored* if you define */ + /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER; in other words, either define */ + /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER or */ + /* TT_CONFIG_OPTION_UNPATENTED_HINTING but not both at the same time. */ + /* */ + /* This macro is only useful for a small number of font files (mostly */ + /* for Asian scripts) that require bytecode interpretation to properly */ + /* load glyphs. For all other fonts, this produces unpleasant results, */ + /* thus the unpatented interpreter is never used to load glyphs from */ + /* TrueType fonts unless one of the following two options is used. */ + /* */ + /* - The unpatented interpreter is explicitly activated by the user */ + /* through the FT_PARAM_TAG_UNPATENTED_HINTING parameter tag */ + /* when opening the FT_Face. */ + /* */ + /* - FreeType detects that the FT_Face corresponds to one of the */ + /* `trick' fonts (e.g., `Mingliu') it knows about. The font engine */ + /* contains a hard-coded list of font names and other matching */ + /* parameters (see function `tt_face_init' in file */ + /* `src/truetype/ttobjs.c'). */ + /* */ + /* Here a sample code snippet for using FT_PARAM_TAG_UNPATENTED_HINTING. */ + /* */ + /* { */ + /* FT_Parameter parameter; */ + /* FT_Open_Args open_args; */ + /* */ + /* */ + /* parameter.tag = FT_PARAM_TAG_UNPATENTED_HINTING; */ + /* */ + /* open_args.flags = FT_OPEN_PATHNAME | FT_OPEN_PARAMS; */ + /* open_args.pathname = my_font_pathname; */ + /* open_args.num_params = 1; */ + /* open_args.params = ¶meter; */ + /* */ + /* error = FT_Open_Face( library, &open_args, index, &face ); */ + /* ... */ + /* } */ + /* */ +#define TT_CONFIG_OPTION_UNPATENTED_HINTING + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_INTERPRETER_SWITCH to compile the TrueType */ + /* bytecode interpreter with a huge switch statement, rather than a call */ + /* table. This results in smaller and faster code for a number of */ + /* architectures. */ + /* */ + /* Note however that on some compiler/processor combinations, undefining */ + /* this macro will generate faster, though larger, code. */ + /* */ +#define TT_CONFIG_OPTION_INTERPRETER_SWITCH + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED to compile the */ + /* TrueType glyph loader to use Apple's definition of how to handle */ + /* component offsets in composite glyphs. */ + /* */ + /* Apple and MS disagree on the default behavior of component offsets */ + /* in composites. Apple says that they should be scaled by the scaling */ + /* factors in the transformation matrix (roughly, it's more complex) */ + /* while MS says they should not. OpenType defines two bits in the */ + /* composite flags array which can be used to disambiguate, but old */ + /* fonts will not have them. */ + /* */ + /* http://partners.adobe.com/asn/developer/opentype/glyf.html */ + /* http://fonts.apple.com/TTRefMan/RM06/Chap6glyf.html */ + /* */ +#undef TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_GX_VAR_SUPPORT if you want to include */ + /* support for Apple's distortable font technology (fvar, gvar, cvar, */ + /* and avar tables). This has many similarities to Type 1 Multiple */ + /* Masters support. */ + /* */ +#define TT_CONFIG_OPTION_GX_VAR_SUPPORT + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_BDF if you want to include support for */ + /* an embedded `BDF ' table within SFNT-based bitmap formats. */ + /* */ +#define TT_CONFIG_OPTION_BDF + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** T Y P E 1 D R I V E R C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* T1_MAX_DICT_DEPTH is the maximal depth of nest dictionaries and */ + /* arrays in the Type 1 stream (see t1load.c). A minimum of 4 is */ + /* required. */ + /* */ +#define T1_MAX_DICT_DEPTH 5 + + + /*************************************************************************/ + /* */ + /* T1_MAX_SUBRS_CALLS details the maximum number of nested sub-routine */ + /* calls during glyph loading. */ + /* */ +#define T1_MAX_SUBRS_CALLS 16 + + + /*************************************************************************/ + /* */ + /* T1_MAX_CHARSTRING_OPERANDS is the charstring stack's capacity. A */ + /* minimum of 16 is required. */ + /* */ + /* The Chinese font MingTiEG-Medium (CNS 11643 character set) needs 256. */ + /* */ +#define T1_MAX_CHARSTRINGS_OPERANDS 256 + + + /*************************************************************************/ + /* */ + /* Define this configuration macro if you want to prevent the */ + /* compilation of `t1afm', which is in charge of reading Type 1 AFM */ + /* files into an existing face. Note that if set, the T1 driver will be */ + /* unable to produce kerning distances. */ + /* */ +#undef T1_CONFIG_OPTION_NO_AFM + + + /*************************************************************************/ + /* */ + /* Define this configuration macro if you want to prevent the */ + /* compilation of the Multiple Masters font support in the Type 1 */ + /* driver. */ + /* */ +#undef T1_CONFIG_OPTION_NO_MM_SUPPORT + + + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** A U T O F I T M O D U L E C O N F I G U R A T I O N ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* Compile autofit module with CJK (Chinese, Japanese, Korean) script */ + /* support. */ + /* */ +#define AF_CONFIG_OPTION_CJK + + /*************************************************************************/ + /* */ + /* Compile autofit module with Indic script support. */ + /* */ +#define AF_CONFIG_OPTION_INDIC + + /* */ + + + /* + * Define this variable if you want to keep the layout of internal + * structures that was used prior to FreeType 2.2. This also compiles in + * a few obsolete functions to avoid linking problems on typical Unix + * distributions. + * + * For embedded systems or building a new distribution from scratch, it + * is recommended to disable the macro since it reduces the library's code + * size and activates a few memory-saving optimizations as well. + */ +#define FT_CONFIG_OPTION_OLD_INTERNALS + + + /* + * This macro is defined if either unpatented or native TrueType + * hinting is requested by the definitions above. + */ +#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER +#define TT_USE_BYTECODE_INTERPRETER +#undef TT_CONFIG_OPTION_UNPATENTED_HINTING +#elif defined TT_CONFIG_OPTION_UNPATENTED_HINTING +#define TT_USE_BYTECODE_INTERPRETER +#endif + +FT_END_HEADER + + +#endif /* __FTOPTION_H__ */ + + +/* END */ diff --git a/guilib/freetype2/include/freetype/config/ftstdlib.h b/guilib/freetype2/include/freetype/config/ftstdlib.h new file mode 100644 index 0000000000..ce5557aef3 --- /dev/null +++ b/guilib/freetype2/include/freetype/config/ftstdlib.h @@ -0,0 +1,172 @@ +/***************************************************************************/ +/* */ +/* ftstdlib.h */ +/* */ +/* ANSI-specific library and header configuration file (specification */ +/* only). */ +/* */ +/* Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2009 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to group all #includes to the ANSI C library that */ + /* FreeType normally requires. It also defines macros to rename the */ + /* standard functions within the FreeType source code. */ + /* */ + /* Load a file which defines __FTSTDLIB_H__ before this one to override */ + /* it. */ + /* */ + /*************************************************************************/ + + +#ifndef __FTSTDLIB_H__ +#define __FTSTDLIB_H__ + + +#include <stddef.h> + +#define ft_ptrdiff_t ptrdiff_t + + + /**********************************************************************/ + /* */ + /* integer limits */ + /* */ + /* UINT_MAX and ULONG_MAX are used to automatically compute the size */ + /* of `int' and `long' in bytes at compile-time. So far, this works */ + /* for all platforms the library has been tested on. */ + /* */ + /* Note that on the extremely rare platforms that do not provide */ + /* integer types that are _exactly_ 16 and 32 bits wide (e.g. some */ + /* old Crays where `int' is 36 bits), we do not make any guarantee */ + /* about the correct behaviour of FT2 with all fonts. */ + /* */ + /* In these case, `ftconfig.h' will refuse to compile anyway with a */ + /* message like `couldn't find 32-bit type' or something similar. */ + /* */ + /**********************************************************************/ + + +#include <limits.h> + +#define FT_CHAR_BIT CHAR_BIT +#define FT_INT_MAX INT_MAX +#define FT_UINT_MAX UINT_MAX +#define FT_ULONG_MAX ULONG_MAX + + + /**********************************************************************/ + /* */ + /* character and string processing */ + /* */ + /**********************************************************************/ + + +#include <string.h> + +#define ft_memchr memchr +#define ft_memcmp memcmp +#define ft_memcpy memcpy +#define ft_memmove memmove +#define ft_memset memset +#define ft_strcat strcat +#define ft_strcmp strcmp +#define ft_strcpy strcpy +#define ft_strlen strlen +#define ft_strncmp strncmp +#define ft_strncpy strncpy +#define ft_strrchr strrchr +#define ft_strstr strstr + + + /**********************************************************************/ + /* */ + /* file handling */ + /* */ + /**********************************************************************/ + + +#include <stdio.h> + +#define FT_FILE FILE +#define ft_fclose fclose +#define ft_fopen fopen +#define ft_fread fread +#define ft_fseek fseek +#define ft_ftell ftell +#define ft_sprintf sprintf + + + /**********************************************************************/ + /* */ + /* sorting */ + /* */ + /**********************************************************************/ + + +#include <stdlib.h> + +#define ft_qsort qsort + + + /**********************************************************************/ + /* */ + /* memory allocation */ + /* */ + /**********************************************************************/ + + +#define ft_scalloc calloc +#define ft_sfree free +#define ft_smalloc malloc +#define ft_srealloc realloc + + + /**********************************************************************/ + /* */ + /* miscellaneous */ + /* */ + /**********************************************************************/ + + +#define ft_atol atol +#define ft_labs labs + + + /**********************************************************************/ + /* */ + /* execution control */ + /* */ + /**********************************************************************/ + + +#include <setjmp.h> + +#define ft_jmp_buf jmp_buf /* note: this cannot be a typedef since */ + /* jmp_buf is defined as a macro */ + /* on certain platforms */ + +#define ft_longjmp longjmp +#define ft_setjmp( b ) setjmp( *(jmp_buf*) &(b) ) /* same thing here */ + + + /* the following is only used for debugging purposes, i.e., if */ + /* FT_DEBUG_LEVEL_ERROR or FT_DEBUG_LEVEL_TRACE are defined */ + +#include <stdarg.h> + + +#endif /* __FTSTDLIB_H__ */ + + +/* END */ diff --git a/guilib/freetype2/include/freetype/freetype.h b/guilib/freetype2/include/freetype/freetype.h new file mode 100644 index 0000000000..364388b5d6 --- /dev/null +++ b/guilib/freetype2/include/freetype/freetype.h @@ -0,0 +1,3862 @@ +/***************************************************************************/ +/* */ +/* freetype.h */ +/* */ +/* FreeType high-level API and common types (specification only). */ +/* */ +/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef FT_FREETYPE_H +#error "`ft2build.h' hasn't been included yet!" +#error "Please always use macros to include FreeType header files." +#error "Example:" +#error " #include <ft2build.h>" +#error " #include FT_FREETYPE_H" +#endif + + +#ifndef __FREETYPE_H__ +#define __FREETYPE_H__ + + +#include <ft2build.h> +#include FT_CONFIG_CONFIG_H +#include FT_ERRORS_H +#include FT_TYPES_H + + +FT_BEGIN_HEADER + + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* user_allocation */ + /* */ + /* <Title> */ + /* User allocation */ + /* */ + /* <Abstract> */ + /* How client applications should allocate FreeType data structures. */ + /* */ + /* <Description> */ + /* FreeType assumes that structures allocated by the user and passed */ + /* as arguments are zeroed out except for the actual data. In other */ + /* words, it is recommended to use `calloc' (or variants of it) */ + /* instead of `malloc' for allocation. */ + /* */ + /*************************************************************************/ + + + + /*************************************************************************/ + /*************************************************************************/ + /* */ + /* B A S I C T Y P E S */ + /* */ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* base_interface */ + /* */ + /* <Title> */ + /* Base Interface */ + /* */ + /* <Abstract> */ + /* The FreeType~2 base font interface. */ + /* */ + /* <Description> */ + /* This section describes the public high-level API of FreeType~2. */ + /* */ + /* <Order> */ + /* FT_Library */ + /* FT_Face */ + /* FT_Size */ + /* FT_GlyphSlot */ + /* FT_CharMap */ + /* FT_Encoding */ + /* */ + /* FT_FaceRec */ + /* */ + /* FT_FACE_FLAG_SCALABLE */ + /* FT_FACE_FLAG_FIXED_SIZES */ + /* FT_FACE_FLAG_FIXED_WIDTH */ + /* FT_FACE_FLAG_HORIZONTAL */ + /* FT_FACE_FLAG_VERTICAL */ + /* FT_FACE_FLAG_SFNT */ + /* FT_FACE_FLAG_KERNING */ + /* FT_FACE_FLAG_MULTIPLE_MASTERS */ + /* FT_FACE_FLAG_GLYPH_NAMES */ + /* FT_FACE_FLAG_EXTERNAL_STREAM */ + /* FT_FACE_FLAG_FAST_GLYPHS */ + /* FT_FACE_FLAG_HINTER */ + /* */ + /* FT_STYLE_FLAG_BOLD */ + /* FT_STYLE_FLAG_ITALIC */ + /* */ + /* FT_SizeRec */ + /* FT_Size_Metrics */ + /* */ + /* FT_GlyphSlotRec */ + /* FT_Glyph_Metrics */ + /* FT_SubGlyph */ + /* */ + /* FT_Bitmap_Size */ + /* */ + /* FT_Init_FreeType */ + /* FT_Done_FreeType */ + /* */ + /* FT_New_Face */ + /* FT_Done_Face */ + /* FT_New_Memory_Face */ + /* FT_Open_Face */ + /* FT_Open_Args */ + /* FT_Parameter */ + /* FT_Attach_File */ + /* FT_Attach_Stream */ + /* */ + /* FT_Set_Char_Size */ + /* FT_Set_Pixel_Sizes */ + /* FT_Request_Size */ + /* FT_Select_Size */ + /* FT_Size_Request_Type */ + /* FT_Size_Request */ + /* FT_Set_Transform */ + /* FT_Load_Glyph */ + /* FT_Get_Char_Index */ + /* FT_Get_Name_Index */ + /* FT_Load_Char */ + /* */ + /* FT_OPEN_MEMORY */ + /* FT_OPEN_STREAM */ + /* FT_OPEN_PATHNAME */ + /* FT_OPEN_DRIVER */ + /* FT_OPEN_PARAMS */ + /* */ + /* FT_LOAD_DEFAULT */ + /* FT_LOAD_RENDER */ + /* FT_LOAD_MONOCHROME */ + /* FT_LOAD_LINEAR_DESIGN */ + /* FT_LOAD_NO_SCALE */ + /* FT_LOAD_NO_HINTING */ + /* FT_LOAD_NO_BITMAP */ + /* FT_LOAD_CROP_BITMAP */ + /* */ + /* FT_LOAD_VERTICAL_LAYOUT */ + /* FT_LOAD_IGNORE_TRANSFORM */ + /* FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH */ + /* FT_LOAD_FORCE_AUTOHINT */ + /* FT_LOAD_NO_RECURSE */ + /* FT_LOAD_PEDANTIC */ + /* */ + /* FT_LOAD_TARGET_NORMAL */ + /* FT_LOAD_TARGET_LIGHT */ + /* FT_LOAD_TARGET_MONO */ + /* FT_LOAD_TARGET_LCD */ + /* FT_LOAD_TARGET_LCD_V */ + /* */ + /* FT_Render_Glyph */ + /* FT_Render_Mode */ + /* FT_Get_Kerning */ + /* FT_Kerning_Mode */ + /* FT_Get_Track_Kerning */ + /* FT_Get_Glyph_Name */ + /* FT_Get_Postscript_Name */ + /* */ + /* FT_CharMapRec */ + /* FT_Select_Charmap */ + /* FT_Set_Charmap */ + /* FT_Get_Charmap_Index */ + /* */ + /* FT_FSTYPE_INSTALLABLE_EMBEDDING */ + /* FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING */ + /* FT_FSTYPE_PREVIEW_AND_PRINT_EMBEDDING */ + /* FT_FSTYPE_EDITABLE_EMBEDDING */ + /* FT_FSTYPE_NO_SUBSETTING */ + /* FT_FSTYPE_BITMAP_EMBEDDING_ONLY */ + /* */ + /* FT_Get_FSType_Flags */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Glyph_Metrics */ + /* */ + /* <Description> */ + /* A structure used to model the metrics of a single glyph. The */ + /* values are expressed in 26.6 fractional pixel format; if the flag */ + /* @FT_LOAD_NO_SCALE has been used while loading the glyph, values */ + /* are expressed in font units instead. */ + /* */ + /* <Fields> */ + /* width :: */ + /* The glyph's width. */ + /* */ + /* height :: */ + /* The glyph's height. */ + /* */ + /* horiBearingX :: */ + /* Left side bearing for horizontal layout. */ + /* */ + /* horiBearingY :: */ + /* Top side bearing for horizontal layout. */ + /* */ + /* horiAdvance :: */ + /* Advance width for horizontal layout. */ + /* */ + /* vertBearingX :: */ + /* Left side bearing for vertical layout. */ + /* */ + /* vertBearingY :: */ + /* Top side bearing for vertical layout. */ + /* */ + /* vertAdvance :: */ + /* Advance height for vertical layout. */ + /* */ + typedef struct FT_Glyph_Metrics_ + { + FT_Pos width; + FT_Pos height; + + FT_Pos horiBearingX; + FT_Pos horiBearingY; + FT_Pos horiAdvance; + + FT_Pos vertBearingX; + FT_Pos vertBearingY; + FT_Pos vertAdvance; + + } FT_Glyph_Metrics; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Bitmap_Size */ + /* */ + /* <Description> */ + /* This structure models the metrics of a bitmap strike (i.e., a set */ + /* of glyphs for a given point size and resolution) in a bitmap font. */ + /* It is used for the `available_sizes' field of @FT_Face. */ + /* */ + /* <Fields> */ + /* height :: The vertical distance, in pixels, between two */ + /* consecutive baselines. It is always positive. */ + /* */ + /* width :: The average width, in pixels, of all glyphs in the */ + /* strike. */ + /* */ + /* size :: The nominal size of the strike in 26.6 fractional */ + /* points. This field is not very useful. */ + /* */ + /* x_ppem :: The horizontal ppem (nominal width) in 26.6 fractional */ + /* pixels. */ + /* */ + /* y_ppem :: The vertical ppem (nominal height) in 26.6 fractional */ + /* pixels. */ + /* */ + /* <Note> */ + /* Windows FNT: */ + /* The nominal size given in a FNT font is not reliable. Thus when */ + /* the driver finds it incorrect, it sets `size' to some calculated */ + /* values and sets `x_ppem' and `y_ppem' to the pixel width and */ + /* height given in the font, respectively. */ + /* */ + /* TrueType embedded bitmaps: */ + /* `size', `width', and `height' values are not contained in the */ + /* bitmap strike itself. They are computed from the global font */ + /* parameters. */ + /* */ + typedef struct FT_Bitmap_Size_ + { + FT_Short height; + FT_Short width; + + FT_Pos size; + + FT_Pos x_ppem; + FT_Pos y_ppem; + + } FT_Bitmap_Size; + + + /*************************************************************************/ + /*************************************************************************/ + /* */ + /* O B J E C T C L A S S E S */ + /* */ + /*************************************************************************/ + /*************************************************************************/ + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Library */ + /* */ + /* <Description> */ + /* A handle to a FreeType library instance. Each `library' is */ + /* completely independent from the others; it is the `root' of a set */ + /* of objects like fonts, faces, sizes, etc. */ + /* */ + /* It also embeds a memory manager (see @FT_Memory), as well as a */ + /* scan-line converter object (see @FT_Raster). */ + /* */ + /* For multi-threading applications each thread should have its own */ + /* FT_Library object. */ + /* */ + /* <Note> */ + /* Library objects are normally created by @FT_Init_FreeType, and */ + /* destroyed with @FT_Done_FreeType. */ + /* */ + typedef struct FT_LibraryRec_ *FT_Library; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Module */ + /* */ + /* <Description> */ + /* A handle to a given FreeType module object. Each module can be a */ + /* font driver, a renderer, or anything else that provides services */ + /* to the formers. */ + /* */ + typedef struct FT_ModuleRec_* FT_Module; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Driver */ + /* */ + /* <Description> */ + /* A handle to a given FreeType font driver object. Each font driver */ + /* is a special module capable of creating faces from font files. */ + /* */ + typedef struct FT_DriverRec_* FT_Driver; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Renderer */ + /* */ + /* <Description> */ + /* A handle to a given FreeType renderer. A renderer is a special */ + /* module in charge of converting a glyph image to a bitmap, when */ + /* necessary. Each renderer supports a given glyph image format, and */ + /* one or more target surface depths. */ + /* */ + typedef struct FT_RendererRec_* FT_Renderer; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Face */ + /* */ + /* <Description> */ + /* A handle to a given typographic face object. A face object models */ + /* a given typeface, in a given style. */ + /* */ + /* <Note> */ + /* Each face object also owns a single @FT_GlyphSlot object, as well */ + /* as one or more @FT_Size objects. */ + /* */ + /* Use @FT_New_Face or @FT_Open_Face to create a new face object from */ + /* a given filepathname or a custom input stream. */ + /* */ + /* Use @FT_Done_Face to destroy it (along with its slot and sizes). */ + /* */ + /* <Also> */ + /* See @FT_FaceRec for the publicly accessible fields of a given face */ + /* object. */ + /* */ + typedef struct FT_FaceRec_* FT_Face; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Size */ + /* */ + /* <Description> */ + /* A handle to an object used to model a face scaled to a given */ + /* character size. */ + /* */ + /* <Note> */ + /* Each @FT_Face has an _active_ @FT_Size object that is used by */ + /* functions like @FT_Load_Glyph to determine the scaling */ + /* transformation which is used to load and hint glyphs and metrics. */ + /* */ + /* You can use @FT_Set_Char_Size, @FT_Set_Pixel_Sizes, */ + /* @FT_Request_Size or even @FT_Select_Size to change the content */ + /* (i.e., the scaling values) of the active @FT_Size. */ + /* */ + /* You can use @FT_New_Size to create additional size objects for a */ + /* given @FT_Face, but they won't be used by other functions until */ + /* you activate it through @FT_Activate_Size. Only one size can be */ + /* activated at any given time per face. */ + /* */ + /* <Also> */ + /* See @FT_SizeRec for the publicly accessible fields of a given size */ + /* object. */ + /* */ + typedef struct FT_SizeRec_* FT_Size; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_GlyphSlot */ + /* */ + /* <Description> */ + /* A handle to a given `glyph slot'. A slot is a container where it */ + /* is possible to load any of the glyphs contained in its parent */ + /* face. */ + /* */ + /* In other words, each time you call @FT_Load_Glyph or */ + /* @FT_Load_Char, the slot's content is erased by the new glyph data, */ + /* i.e., the glyph's metrics, its image (bitmap or outline), and */ + /* other control information. */ + /* */ + /* <Also> */ + /* See @FT_GlyphSlotRec for the publicly accessible glyph fields. */ + /* */ + typedef struct FT_GlyphSlotRec_* FT_GlyphSlot; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_CharMap */ + /* */ + /* <Description> */ + /* A handle to a given character map. A charmap is used to translate */ + /* character codes in a given encoding into glyph indexes for its */ + /* parent's face. Some font formats may provide several charmaps per */ + /* font. */ + /* */ + /* Each face object owns zero or more charmaps, but only one of them */ + /* can be `active' and used by @FT_Get_Char_Index or @FT_Load_Char. */ + /* */ + /* The list of available charmaps in a face is available through the */ + /* `face->num_charmaps' and `face->charmaps' fields of @FT_FaceRec. */ + /* */ + /* The currently active charmap is available as `face->charmap'. */ + /* You should call @FT_Set_Charmap to change it. */ + /* */ + /* <Note> */ + /* When a new face is created (either through @FT_New_Face or */ + /* @FT_Open_Face), the library looks for a Unicode charmap within */ + /* the list and automatically activates it. */ + /* */ + /* <Also> */ + /* See @FT_CharMapRec for the publicly accessible fields of a given */ + /* character map. */ + /* */ + typedef struct FT_CharMapRec_* FT_CharMap; + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_ENC_TAG */ + /* */ + /* <Description> */ + /* This macro converts four-letter tags into an unsigned long. It is */ + /* used to define `encoding' identifiers (see @FT_Encoding). */ + /* */ + /* <Note> */ + /* Since many 16-bit compilers don't like 32-bit enumerations, you */ + /* should redefine this macro in case of problems to something like */ + /* this: */ + /* */ + /* { */ + /* #define FT_ENC_TAG( value, a, b, c, d ) value */ + /* } */ + /* */ + /* to get a simple enumeration without assigning special numbers. */ + /* */ + +#ifndef FT_ENC_TAG +#define FT_ENC_TAG( value, a, b, c, d ) \ + value = ( ( (FT_UInt32)(a) << 24 ) | \ + ( (FT_UInt32)(b) << 16 ) | \ + ( (FT_UInt32)(c) << 8 ) | \ + (FT_UInt32)(d) ) + +#endif /* FT_ENC_TAG */ + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Encoding */ + /* */ + /* <Description> */ + /* An enumeration used to specify character sets supported by */ + /* charmaps. Used in the @FT_Select_Charmap API function. */ + /* */ + /* <Note> */ + /* Despite the name, this enumeration lists specific character */ + /* repertories (i.e., charsets), and not text encoding methods (e.g., */ + /* UTF-8, UTF-16, GB2312_EUC, etc.). */ + /* */ + /* Because of 32-bit charcodes defined in Unicode (i.e., surrogates), */ + /* all character codes must be expressed as FT_Longs. */ + /* */ + /* Other encodings might be defined in the future. */ + /* */ + /* <Values> */ + /* FT_ENCODING_NONE :: */ + /* The encoding value~0 is reserved. */ + /* */ + /* FT_ENCODING_UNICODE :: */ + /* Corresponds to the Unicode character set. This value covers */ + /* all versions of the Unicode repertoire, including ASCII and */ + /* Latin-1. Most fonts include a Unicode charmap, but not all */ + /* of them. */ + /* */ + /* FT_ENCODING_MS_SYMBOL :: */ + /* Corresponds to the Microsoft Symbol encoding, used to encode */ + /* mathematical symbols in the 32..255 character code range. For */ + /* more information, see `http://www.ceviz.net/symbol.htm'. */ + /* */ + /* FT_ENCODING_SJIS :: */ + /* Corresponds to Japanese SJIS encoding. More info at */ + /* at `http://langsupport.japanreference.com/encoding.shtml'. */ + /* See note on multi-byte encodings below. */ + /* */ + /* FT_ENCODING_GB2312 :: */ + /* Corresponds to an encoding system for Simplified Chinese as used */ + /* used in mainland China. */ + /* */ + /* FT_ENCODING_BIG5 :: */ + /* Corresponds to an encoding system for Traditional Chinese as */ + /* used in Taiwan and Hong Kong. */ + /* */ + /* FT_ENCODING_WANSUNG :: */ + /* Corresponds to the Korean encoding system known as Wansung. */ + /* For more information see */ + /* `http://www.microsoft.com/typography/unicode/949.txt'. */ + /* */ + /* FT_ENCODING_JOHAB :: */ + /* The Korean standard character set (KS~C 5601-1992), which */ + /* corresponds to MS Windows code page 1361. This character set */ + /* includes all possible Hangeul character combinations. */ + /* */ + /* FT_ENCODING_ADOBE_LATIN_1 :: */ + /* Corresponds to a Latin-1 encoding as defined in a Type~1 */ + /* PostScript font. It is limited to 256 character codes. */ + /* */ + /* FT_ENCODING_ADOBE_STANDARD :: */ + /* Corresponds to the Adobe Standard encoding, as found in Type~1, */ + /* CFF, and OpenType/CFF fonts. It is limited to 256 character */ + /* codes. */ + /* */ + /* FT_ENCODING_ADOBE_EXPERT :: */ + /* Corresponds to the Adobe Expert encoding, as found in Type~1, */ + /* CFF, and OpenType/CFF fonts. It is limited to 256 character */ + /* codes. */ + /* */ + /* FT_ENCODING_ADOBE_CUSTOM :: */ + /* Corresponds to a custom encoding, as found in Type~1, CFF, and */ + /* OpenType/CFF fonts. It is limited to 256 character codes. */ + /* */ + /* FT_ENCODING_APPLE_ROMAN :: */ + /* Corresponds to the 8-bit Apple roman encoding. Many TrueType */ + /* and OpenType fonts contain a charmap for this encoding, since */ + /* older versions of Mac OS are able to use it. */ + /* */ + /* FT_ENCODING_OLD_LATIN_2 :: */ + /* This value is deprecated and was never used nor reported by */ + /* FreeType. Don't use or test for it. */ + /* */ + /* FT_ENCODING_MS_SJIS :: */ + /* Same as FT_ENCODING_SJIS. Deprecated. */ + /* */ + /* FT_ENCODING_MS_GB2312 :: */ + /* Same as FT_ENCODING_GB2312. Deprecated. */ + /* */ + /* FT_ENCODING_MS_BIG5 :: */ + /* Same as FT_ENCODING_BIG5. Deprecated. */ + /* */ + /* FT_ENCODING_MS_WANSUNG :: */ + /* Same as FT_ENCODING_WANSUNG. Deprecated. */ + /* */ + /* FT_ENCODING_MS_JOHAB :: */ + /* Same as FT_ENCODING_JOHAB. Deprecated. */ + /* */ + /* <Note> */ + /* By default, FreeType automatically synthesizes a Unicode charmap */ + /* for PostScript fonts, using their glyph names dictionaries. */ + /* However, it also reports the encodings defined explicitly in the */ + /* font file, for the cases when they are needed, with the Adobe */ + /* values as well. */ + /* */ + /* FT_ENCODING_NONE is set by the BDF and PCF drivers if the charmap */ + /* is neither Unicode nor ISO-8859-1 (otherwise it is set to */ + /* FT_ENCODING_UNICODE). Use @FT_Get_BDF_Charset_ID to find out */ + /* which encoding is really present. If, for example, the */ + /* `cs_registry' field is `KOI8' and the `cs_encoding' field is `R', */ + /* the font is encoded in KOI8-R. */ + /* */ + /* FT_ENCODING_NONE is always set (with a single exception) by the */ + /* winfonts driver. Use @FT_Get_WinFNT_Header and examine the */ + /* `charset' field of the @FT_WinFNT_HeaderRec structure to find out */ + /* which encoding is really present. For example, */ + /* @FT_WinFNT_ID_CP1251 (204) means Windows code page 1251 (for */ + /* Russian). */ + /* */ + /* FT_ENCODING_NONE is set if `platform_id' is @TT_PLATFORM_MACINTOSH */ + /* and `encoding_id' is not @TT_MAC_ID_ROMAN (otherwise it is set to */ + /* FT_ENCODING_APPLE_ROMAN). */ + /* */ + /* If `platform_id' is @TT_PLATFORM_MACINTOSH, use the function */ + /* @FT_Get_CMap_Language_ID to query the Mac language ID which may */ + /* be needed to be able to distinguish Apple encoding variants. See */ + /* */ + /* http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/README.TXT */ + /* */ + /* to get an idea how to do that. Basically, if the language ID */ + /* is~0, don't use it, otherwise subtract 1 from the language ID. */ + /* Then examine `encoding_id'. If, for example, `encoding_id' is */ + /* @TT_MAC_ID_ROMAN and the language ID (minus~1) is */ + /* `TT_MAC_LANGID_GREEK', it is the Greek encoding, not Roman. */ + /* @TT_MAC_ID_ARABIC with `TT_MAC_LANGID_FARSI' means the Farsi */ + /* variant the Arabic encoding. */ + /* */ + typedef enum FT_Encoding_ + { + FT_ENC_TAG( FT_ENCODING_NONE, 0, 0, 0, 0 ), + + FT_ENC_TAG( FT_ENCODING_MS_SYMBOL, 's', 'y', 'm', 'b' ), + FT_ENC_TAG( FT_ENCODING_UNICODE, 'u', 'n', 'i', 'c' ), + + FT_ENC_TAG( FT_ENCODING_SJIS, 's', 'j', 'i', 's' ), + FT_ENC_TAG( FT_ENCODING_GB2312, 'g', 'b', ' ', ' ' ), + FT_ENC_TAG( FT_ENCODING_BIG5, 'b', 'i', 'g', '5' ), + FT_ENC_TAG( FT_ENCODING_WANSUNG, 'w', 'a', 'n', 's' ), + FT_ENC_TAG( FT_ENCODING_JOHAB, 'j', 'o', 'h', 'a' ), + + /* for backwards compatibility */ + FT_ENCODING_MS_SJIS = FT_ENCODING_SJIS, + FT_ENCODING_MS_GB2312 = FT_ENCODING_GB2312, + FT_ENCODING_MS_BIG5 = FT_ENCODING_BIG5, + FT_ENCODING_MS_WANSUNG = FT_ENCODING_WANSUNG, + FT_ENCODING_MS_JOHAB = FT_ENCODING_JOHAB, + + FT_ENC_TAG( FT_ENCODING_ADOBE_STANDARD, 'A', 'D', 'O', 'B' ), + FT_ENC_TAG( FT_ENCODING_ADOBE_EXPERT, 'A', 'D', 'B', 'E' ), + FT_ENC_TAG( FT_ENCODING_ADOBE_CUSTOM, 'A', 'D', 'B', 'C' ), + FT_ENC_TAG( FT_ENCODING_ADOBE_LATIN_1, 'l', 'a', 't', '1' ), + + FT_ENC_TAG( FT_ENCODING_OLD_LATIN_2, 'l', 'a', 't', '2' ), + + FT_ENC_TAG( FT_ENCODING_APPLE_ROMAN, 'a', 'r', 'm', 'n' ) + + } FT_Encoding; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* ft_encoding_xxx */ + /* */ + /* <Description> */ + /* These constants are deprecated; use the corresponding @FT_Encoding */ + /* values instead. */ + /* */ +#define ft_encoding_none FT_ENCODING_NONE +#define ft_encoding_unicode FT_ENCODING_UNICODE +#define ft_encoding_symbol FT_ENCODING_MS_SYMBOL +#define ft_encoding_latin_1 FT_ENCODING_ADOBE_LATIN_1 +#define ft_encoding_latin_2 FT_ENCODING_OLD_LATIN_2 +#define ft_encoding_sjis FT_ENCODING_SJIS +#define ft_encoding_gb2312 FT_ENCODING_GB2312 +#define ft_encoding_big5 FT_ENCODING_BIG5 +#define ft_encoding_wansung FT_ENCODING_WANSUNG +#define ft_encoding_johab FT_ENCODING_JOHAB + +#define ft_encoding_adobe_standard FT_ENCODING_ADOBE_STANDARD +#define ft_encoding_adobe_expert FT_ENCODING_ADOBE_EXPERT +#define ft_encoding_adobe_custom FT_ENCODING_ADOBE_CUSTOM +#define ft_encoding_apple_roman FT_ENCODING_APPLE_ROMAN + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_CharMapRec */ + /* */ + /* <Description> */ + /* The base charmap structure. */ + /* */ + /* <Fields> */ + /* face :: A handle to the parent face object. */ + /* */ + /* encoding :: An @FT_Encoding tag identifying the charmap. Use */ + /* this with @FT_Select_Charmap. */ + /* */ + /* platform_id :: An ID number describing the platform for the */ + /* following encoding ID. This comes directly from */ + /* the TrueType specification and should be emulated */ + /* for other formats. */ + /* */ + /* encoding_id :: A platform specific encoding number. This also */ + /* comes from the TrueType specification and should be */ + /* emulated similarly. */ + /* */ + typedef struct FT_CharMapRec_ + { + FT_Face face; + FT_Encoding encoding; + FT_UShort platform_id; + FT_UShort encoding_id; + + } FT_CharMapRec; + + + /*************************************************************************/ + /*************************************************************************/ + /* */ + /* B A S E O B J E C T C L A S S E S */ + /* */ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Face_Internal */ + /* */ + /* <Description> */ + /* An opaque handle to an `FT_Face_InternalRec' structure, used to */ + /* model private data of a given @FT_Face object. */ + /* */ + /* This structure might change between releases of FreeType~2 and is */ + /* not generally available to client applications. */ + /* */ + typedef struct FT_Face_InternalRec_* FT_Face_Internal; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_FaceRec */ + /* */ + /* <Description> */ + /* FreeType root face class structure. A face object models a */ + /* typeface in a font file. */ + /* */ + /* <Fields> */ + /* num_faces :: The number of faces in the font file. Some */ + /* font formats can have multiple faces in */ + /* a font file. */ + /* */ + /* face_index :: The index of the face in the font file. It */ + /* is set to~0 if there is only one face in */ + /* the font file. */ + /* */ + /* face_flags :: A set of bit flags that give important */ + /* information about the face; see */ + /* @FT_FACE_FLAG_XXX for the details. */ + /* */ + /* style_flags :: A set of bit flags indicating the style of */ + /* the face; see @FT_STYLE_FLAG_XXX for the */ + /* details. */ + /* */ + /* num_glyphs :: The number of glyphs in the face. If the */ + /* face is scalable and has sbits (see */ + /* `num_fixed_sizes'), it is set to the number */ + /* of outline glyphs. */ + /* */ + /* For CID-keyed fonts, this value gives the */ + /* highest CID used in the font. */ + /* */ + /* family_name :: The face's family name. This is an ASCII */ + /* string, usually in English, which describes */ + /* the typeface's family (like `Times New */ + /* Roman', `Bodoni', `Garamond', etc). This */ + /* is a least common denominator used to list */ + /* fonts. Some formats (TrueType & OpenType) */ + /* provide localized and Unicode versions of */ + /* this string. Applications should use the */ + /* format specific interface to access them. */ + /* Can be NULL (e.g., in fonts embedded in a */ + /* PDF file). */ + /* */ + /* style_name :: The face's style name. This is an ASCII */ + /* string, usually in English, which describes */ + /* the typeface's style (like `Italic', */ + /* `Bold', `Condensed', etc). Not all font */ + /* formats provide a style name, so this field */ + /* is optional, and can be set to NULL. As */ + /* for `family_name', some formats provide */ + /* localized and Unicode versions of this */ + /* string. Applications should use the format */ + /* specific interface to access them. */ + /* */ + /* num_fixed_sizes :: The number of bitmap strikes in the face. */ + /* Even if the face is scalable, there might */ + /* still be bitmap strikes, which are called */ + /* `sbits' in that case. */ + /* */ + /* available_sizes :: An array of @FT_Bitmap_Size for all bitmap */ + /* strikes in the face. It is set to NULL if */ + /* there is no bitmap strike. */ + /* */ + /* num_charmaps :: The number of charmaps in the face. */ + /* */ + /* charmaps :: An array of the charmaps of the face. */ + /* */ + /* generic :: A field reserved for client uses. See the */ + /* @FT_Generic type description. */ + /* */ + /* bbox :: The font bounding box. Coordinates are */ + /* expressed in font units (see */ + /* `units_per_EM'). The box is large enough */ + /* to contain any glyph from the font. Thus, */ + /* `bbox.yMax' can be seen as the `maximal */ + /* ascender', and `bbox.yMin' as the `minimal */ + /* descender'. Only relevant for scalable */ + /* formats. */ + /* */ + /* Note that the bounding box might be off by */ + /* (at least) one pixel for hinted fonts. See */ + /* @FT_Size_Metrics for further discussion. */ + /* */ + /* units_per_EM :: The number of font units per EM square for */ + /* this face. This is typically 2048 for */ + /* TrueType fonts, and 1000 for Type~1 fonts. */ + /* Only relevant for scalable formats. */ + /* */ + /* ascender :: The typographic ascender of the face, */ + /* expressed in font units. For font formats */ + /* not having this information, it is set to */ + /* `bbox.yMax'. Only relevant for scalable */ + /* formats. */ + /* */ + /* descender :: The typographic descender of the face, */ + /* expressed in font units. For font formats */ + /* not having this information, it is set to */ + /* `bbox.yMin'. Note that this field is */ + /* usually negative. Only relevant for */ + /* scalable formats. */ + /* */ + /* height :: The height is the vertical distance */ + /* between two consecutive baselines, */ + /* expressed in font units. It is always */ + /* positive. Only relevant for scalable */ + /* formats. */ + /* */ + /* max_advance_width :: The maximal advance width, in font units, */ + /* for all glyphs in this face. This can be */ + /* used to make word wrapping computations */ + /* faster. Only relevant for scalable */ + /* formats. */ + /* */ + /* max_advance_height :: The maximal advance height, in font units, */ + /* for all glyphs in this face. This is only */ + /* relevant for vertical layouts, and is set */ + /* to `height' for fonts that do not provide */ + /* vertical metrics. Only relevant for */ + /* scalable formats. */ + /* */ + /* underline_position :: The position, in font units, of the */ + /* underline line for this face. It is the */ + /* center of the underlining stem. Only */ + /* relevant for scalable formats. */ + /* */ + /* underline_thickness :: The thickness, in font units, of the */ + /* underline for this face. Only relevant for */ + /* scalable formats. */ + /* */ + /* glyph :: The face's associated glyph slot(s). */ + /* */ + /* size :: The current active size for this face. */ + /* */ + /* charmap :: The current active charmap for this face. */ + /* */ + /* <Note> */ + /* Fields may be changed after a call to @FT_Attach_File or */ + /* @FT_Attach_Stream. */ + /* */ + typedef struct FT_FaceRec_ + { + FT_Long num_faces; + FT_Long face_index; + + FT_Long face_flags; + FT_Long style_flags; + + FT_Long num_glyphs; + + FT_String* family_name; + FT_String* style_name; + + FT_Int num_fixed_sizes; + FT_Bitmap_Size* available_sizes; + + FT_Int num_charmaps; + FT_CharMap* charmaps; + + FT_Generic generic; + + /*# The following member variables (down to `underline_thickness') */ + /*# are only relevant to scalable outlines; cf. @FT_Bitmap_Size */ + /*# for bitmap fonts. */ + FT_BBox bbox; + + FT_UShort units_per_EM; + FT_Short ascender; + FT_Short descender; + FT_Short height; + + FT_Short max_advance_width; + FT_Short max_advance_height; + + FT_Short underline_position; + FT_Short underline_thickness; + + FT_GlyphSlot glyph; + FT_Size size; + FT_CharMap charmap; + + /*@private begin */ + + FT_Driver driver; + FT_Memory memory; + FT_Stream stream; + + FT_ListRec sizes_list; + + FT_Generic autohint; + void* extensions; + + FT_Face_Internal internal; + + /*@private end */ + + } FT_FaceRec; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_FACE_FLAG_XXX */ + /* */ + /* <Description> */ + /* A list of bit flags used in the `face_flags' field of the */ + /* @FT_FaceRec structure. They inform client applications of */ + /* properties of the corresponding face. */ + /* */ + /* <Values> */ + /* FT_FACE_FLAG_SCALABLE :: */ + /* Indicates that the face contains outline glyphs. This doesn't */ + /* prevent bitmap strikes, i.e., a face can have both this and */ + /* and @FT_FACE_FLAG_FIXED_SIZES set. */ + /* */ + /* FT_FACE_FLAG_FIXED_SIZES :: */ + /* Indicates that the face contains bitmap strikes. See also the */ + /* `num_fixed_sizes' and `available_sizes' fields of @FT_FaceRec. */ + /* */ + /* FT_FACE_FLAG_FIXED_WIDTH :: */ + /* Indicates that the face contains fixed-width characters (like */ + /* Courier, Lucido, MonoType, etc.). */ + /* */ + /* FT_FACE_FLAG_SFNT :: */ + /* Indicates that the face uses the `sfnt' storage scheme. For */ + /* now, this means TrueType and OpenType. */ + /* */ + /* FT_FACE_FLAG_HORIZONTAL :: */ + /* Indicates that the face contains horizontal glyph metrics. This */ + /* should be set for all common formats. */ + /* */ + /* FT_FACE_FLAG_VERTICAL :: */ + /* Indicates that the face contains vertical glyph metrics. This */ + /* is only available in some formats, not all of them. */ + /* */ + /* FT_FACE_FLAG_KERNING :: */ + /* Indicates that the face contains kerning information. If set, */ + /* the kerning distance can be retrieved through the function */ + /* @FT_Get_Kerning. Otherwise the function always return the */ + /* vector (0,0). Note that FreeType doesn't handle kerning data */ + /* from the `GPOS' table (as present in some OpenType fonts). */ + /* */ + /* FT_FACE_FLAG_FAST_GLYPHS :: */ + /* THIS FLAG IS DEPRECATED. DO NOT USE OR TEST IT. */ + /* */ + /* FT_FACE_FLAG_MULTIPLE_MASTERS :: */ + /* Indicates that the font contains multiple masters and is capable */ + /* of interpolating between them. See the multiple-masters */ + /* specific API for details. */ + /* */ + /* FT_FACE_FLAG_GLYPH_NAMES :: */ + /* Indicates that the font contains glyph names that can be */ + /* retrieved through @FT_Get_Glyph_Name. Note that some TrueType */ + /* fonts contain broken glyph name tables. Use the function */ + /* @FT_Has_PS_Glyph_Names when needed. */ + /* */ + /* FT_FACE_FLAG_EXTERNAL_STREAM :: */ + /* Used internally by FreeType to indicate that a face's stream was */ + /* provided by the client application and should not be destroyed */ + /* when @FT_Done_Face is called. Don't read or test this flag. */ + /* */ + /* FT_FACE_FLAG_HINTER :: */ + /* Set if the font driver has a hinting machine of its own. For */ + /* example, with TrueType fonts, it makes sense to use data from */ + /* the SFNT `gasp' table only if the native TrueType hinting engine */ + /* (with the bytecode interpreter) is available and active. */ + /* */ + /* FT_FACE_FLAG_CID_KEYED :: */ + /* Set if the font is CID-keyed. In that case, the font is not */ + /* accessed by glyph indices but by CID values. For subsetted */ + /* CID-keyed fonts this has the consequence that not all index */ + /* values are a valid argument to FT_Load_Glyph. Only the CID */ + /* values for which corresponding glyphs in the subsetted font */ + /* exist make FT_Load_Glyph return successfully; in all other cases */ + /* you get an `FT_Err_Invalid_Argument' error. */ + /* */ + /* Note that CID-keyed fonts which are in an SFNT wrapper don't */ + /* have this flag set since the glyphs are accessed in the normal */ + /* way (using contiguous indices); the `CID-ness' isn't visible to */ + /* the application. */ + /* */ + /* FT_FACE_FLAG_TRICKY :: */ + /* Set if the font is `tricky', this is, it always needs the */ + /* font format's native hinting engine to get a reasonable result. */ + /* A typical example is the Chinese font `mingli.ttf' which uses */ + /* TrueType bytecode instructions to move and scale all of its */ + /* subglyphs. */ + /* */ + /* It is not possible to autohint such fonts using */ + /* @FT_LOAD_FORCE_AUTOHINT; it will also ignore */ + /* @FT_LOAD_NO_HINTING. You have to set both FT_LOAD_NO_HINTING */ + /* and @FT_LOAD_NO_AUTOHINT to really disable hinting; however, you */ + /* probably never want this except for demonstration purposes. */ + /* */ + /* Currently, there are six TrueType fonts in the list of tricky */ + /* fonts; they are hard-coded in file `ttobjs.c'. */ + /* */ +#define FT_FACE_FLAG_SCALABLE ( 1L << 0 ) +#define FT_FACE_FLAG_FIXED_SIZES ( 1L << 1 ) +#define FT_FACE_FLAG_FIXED_WIDTH ( 1L << 2 ) +#define FT_FACE_FLAG_SFNT ( 1L << 3 ) +#define FT_FACE_FLAG_HORIZONTAL ( 1L << 4 ) +#define FT_FACE_FLAG_VERTICAL ( 1L << 5 ) +#define FT_FACE_FLAG_KERNING ( 1L << 6 ) +#define FT_FACE_FLAG_FAST_GLYPHS ( 1L << 7 ) +#define FT_FACE_FLAG_MULTIPLE_MASTERS ( 1L << 8 ) +#define FT_FACE_FLAG_GLYPH_NAMES ( 1L << 9 ) +#define FT_FACE_FLAG_EXTERNAL_STREAM ( 1L << 10 ) +#define FT_FACE_FLAG_HINTER ( 1L << 11 ) +#define FT_FACE_FLAG_CID_KEYED ( 1L << 12 ) +#define FT_FACE_FLAG_TRICKY ( 1L << 13 ) + + + /************************************************************************* + * + * @macro: + * FT_HAS_HORIZONTAL( face ) + * + * @description: + * A macro that returns true whenever a face object contains + * horizontal metrics (this is true for all font formats though). + * + * @also: + * @FT_HAS_VERTICAL can be used to check for vertical metrics. + * + */ +#define FT_HAS_HORIZONTAL( face ) \ + ( face->face_flags & FT_FACE_FLAG_HORIZONTAL ) + + + /************************************************************************* + * + * @macro: + * FT_HAS_VERTICAL( face ) + * + * @description: + * A macro that returns true whenever a face object contains vertical + * metrics. + * + */ +#define FT_HAS_VERTICAL( face ) \ + ( face->face_flags & FT_FACE_FLAG_VERTICAL ) + + + /************************************************************************* + * + * @macro: + * FT_HAS_KERNING( face ) + * + * @description: + * A macro that returns true whenever a face object contains kerning + * data that can be accessed with @FT_Get_Kerning. + * + */ +#define FT_HAS_KERNING( face ) \ + ( face->face_flags & FT_FACE_FLAG_KERNING ) + + + /************************************************************************* + * + * @macro: + * FT_IS_SCALABLE( face ) + * + * @description: + * A macro that returns true whenever a face object contains a scalable + * font face (true for TrueType, Type~1, Type~42, CID, OpenType/CFF, + * and PFR font formats. + * + */ +#define FT_IS_SCALABLE( face ) \ + ( face->face_flags & FT_FACE_FLAG_SCALABLE ) + + + /************************************************************************* + * + * @macro: + * FT_IS_SFNT( face ) + * + * @description: + * A macro that returns true whenever a face object contains a font + * whose format is based on the SFNT storage scheme. This usually + * means: TrueType fonts, OpenType fonts, as well as SFNT-based embedded + * bitmap fonts. + * + * If this macro is true, all functions defined in @FT_SFNT_NAMES_H and + * @FT_TRUETYPE_TABLES_H are available. + * + */ +#define FT_IS_SFNT( face ) \ + ( face->face_flags & FT_FACE_FLAG_SFNT ) + + + /************************************************************************* + * + * @macro: + * FT_IS_FIXED_WIDTH( face ) + * + * @description: + * A macro that returns true whenever a face object contains a font face + * that contains fixed-width (or `monospace', `fixed-pitch', etc.) + * glyphs. + * + */ +#define FT_IS_FIXED_WIDTH( face ) \ + ( face->face_flags & FT_FACE_FLAG_FIXED_WIDTH ) + + + /************************************************************************* + * + * @macro: + * FT_HAS_FIXED_SIZES( face ) + * + * @description: + * A macro that returns true whenever a face object contains some + * embedded bitmaps. See the `available_sizes' field of the + * @FT_FaceRec structure. + * + */ +#define FT_HAS_FIXED_SIZES( face ) \ + ( face->face_flags & FT_FACE_FLAG_FIXED_SIZES ) + + + /************************************************************************* + * + * @macro: + * FT_HAS_FAST_GLYPHS( face ) + * + * @description: + * Deprecated. + * + */ +#define FT_HAS_FAST_GLYPHS( face ) 0 + + + /************************************************************************* + * + * @macro: + * FT_HAS_GLYPH_NAMES( face ) + * + * @description: + * A macro that returns true whenever a face object contains some glyph + * names that can be accessed through @FT_Get_Glyph_Name. + * + */ +#define FT_HAS_GLYPH_NAMES( face ) \ + ( face->face_flags & FT_FACE_FLAG_GLYPH_NAMES ) + + + /************************************************************************* + * + * @macro: + * FT_HAS_MULTIPLE_MASTERS( face ) + * + * @description: + * A macro that returns true whenever a face object contains some + * multiple masters. The functions provided by @FT_MULTIPLE_MASTERS_H + * are then available to choose the exact design you want. + * + */ +#define FT_HAS_MULTIPLE_MASTERS( face ) \ + ( face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS ) + + + /************************************************************************* + * + * @macro: + * FT_IS_CID_KEYED( face ) + * + * @description: + * A macro that returns true whenever a face object contains a CID-keyed + * font. See the discussion of @FT_FACE_FLAG_CID_KEYED for more + * details. + * + * If this macro is true, all functions defined in @FT_CID_H are + * available. + * + */ +#define FT_IS_CID_KEYED( face ) \ + ( face->face_flags & FT_FACE_FLAG_CID_KEYED ) + + + /************************************************************************* + * + * @macro: + * FT_IS_TRICKY( face ) + * + * @description: + * A macro that returns true whenever a face represents a `tricky' font. + * See the discussion of @FT_FACE_FLAG_TRICKY for more details. + * + */ +#define FT_IS_TRICKY( face ) \ + ( face->face_flags & FT_FACE_FLAG_TRICKY ) + + + /*************************************************************************/ + /* */ + /* <Const> */ + /* FT_STYLE_FLAG_XXX */ + /* */ + /* <Description> */ + /* A list of bit-flags used to indicate the style of a given face. */ + /* These are used in the `style_flags' field of @FT_FaceRec. */ + /* */ + /* <Values> */ + /* FT_STYLE_FLAG_ITALIC :: */ + /* Indicates that a given face style is italic or oblique. */ + /* */ + /* FT_STYLE_FLAG_BOLD :: */ + /* Indicates that a given face is bold. */ + /* */ + /* <Note> */ + /* The style information as provided by FreeType is very basic. More */ + /* details are beyond the scope and should be done on a higher level */ + /* (for example, by analyzing various fields of the `OS/2' table in */ + /* SFNT based fonts). */ + /* */ +#define FT_STYLE_FLAG_ITALIC ( 1 << 0 ) +#define FT_STYLE_FLAG_BOLD ( 1 << 1 ) + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Size_Internal */ + /* */ + /* <Description> */ + /* An opaque handle to an `FT_Size_InternalRec' structure, used to */ + /* model private data of a given @FT_Size object. */ + /* */ + typedef struct FT_Size_InternalRec_* FT_Size_Internal; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Size_Metrics */ + /* */ + /* <Description> */ + /* The size metrics structure gives the metrics of a size object. */ + /* */ + /* <Fields> */ + /* x_ppem :: The width of the scaled EM square in pixels, hence */ + /* the term `ppem' (pixels per EM). It is also */ + /* referred to as `nominal width'. */ + /* */ + /* y_ppem :: The height of the scaled EM square in pixels, */ + /* hence the term `ppem' (pixels per EM). It is also */ + /* referred to as `nominal height'. */ + /* */ + /* x_scale :: A 16.16 fractional scaling value used to convert */ + /* horizontal metrics from font units to 26.6 */ + /* fractional pixels. Only relevant for scalable */ + /* font formats. */ + /* */ + /* y_scale :: A 16.16 fractional scaling value used to convert */ + /* vertical metrics from font units to 26.6 */ + /* fractional pixels. Only relevant for scalable */ + /* font formats. */ + /* */ + /* ascender :: The ascender in 26.6 fractional pixels. See */ + /* @FT_FaceRec for the details. */ + /* */ + /* descender :: The descender in 26.6 fractional pixels. See */ + /* @FT_FaceRec for the details. */ + /* */ + /* height :: The height in 26.6 fractional pixels. See */ + /* @FT_FaceRec for the details. */ + /* */ + /* max_advance :: The maximal advance width in 26.6 fractional */ + /* pixels. See @FT_FaceRec for the details. */ + /* */ + /* <Note> */ + /* The scaling values, if relevant, are determined first during a */ + /* size changing operation. The remaining fields are then set by the */ + /* driver. For scalable formats, they are usually set to scaled */ + /* values of the corresponding fields in @FT_FaceRec. */ + /* */ + /* Note that due to glyph hinting, these values might not be exact */ + /* for certain fonts. Thus they must be treated as unreliable */ + /* with an error margin of at least one pixel! */ + /* */ + /* Indeed, the only way to get the exact metrics is to render _all_ */ + /* glyphs. As this would be a definite performance hit, it is up to */ + /* client applications to perform such computations. */ + /* */ + /* The FT_Size_Metrics structure is valid for bitmap fonts also. */ + /* */ + typedef struct FT_Size_Metrics_ + { + FT_UShort x_ppem; /* horizontal pixels per EM */ + FT_UShort y_ppem; /* vertical pixels per EM */ + + FT_Fixed x_scale; /* scaling values used to convert font */ + FT_Fixed y_scale; /* units to 26.6 fractional pixels */ + + FT_Pos ascender; /* ascender in 26.6 frac. pixels */ + FT_Pos descender; /* descender in 26.6 frac. pixels */ + FT_Pos height; /* text height in 26.6 frac. pixels */ + FT_Pos max_advance; /* max horizontal advance, in 26.6 pixels */ + + } FT_Size_Metrics; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_SizeRec */ + /* */ + /* <Description> */ + /* FreeType root size class structure. A size object models a face */ + /* object at a given size. */ + /* */ + /* <Fields> */ + /* face :: Handle to the parent face object. */ + /* */ + /* generic :: A typeless pointer, which is unused by the FreeType */ + /* library or any of its drivers. It can be used by */ + /* client applications to link their own data to each size */ + /* object. */ + /* */ + /* metrics :: Metrics for this size object. This field is read-only. */ + /* */ + typedef struct FT_SizeRec_ + { + FT_Face face; /* parent face object */ + FT_Generic generic; /* generic pointer for client uses */ + FT_Size_Metrics metrics; /* size metrics */ + FT_Size_Internal internal; + + } FT_SizeRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_SubGlyph */ + /* */ + /* <Description> */ + /* The subglyph structure is an internal object used to describe */ + /* subglyphs (for example, in the case of composites). */ + /* */ + /* <Note> */ + /* The subglyph implementation is not part of the high-level API, */ + /* hence the forward structure declaration. */ + /* */ + /* You can however retrieve subglyph information with */ + /* @FT_Get_SubGlyph_Info. */ + /* */ + typedef struct FT_SubGlyphRec_* FT_SubGlyph; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Slot_Internal */ + /* */ + /* <Description> */ + /* An opaque handle to an `FT_Slot_InternalRec' structure, used to */ + /* model private data of a given @FT_GlyphSlot object. */ + /* */ + typedef struct FT_Slot_InternalRec_* FT_Slot_Internal; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_GlyphSlotRec */ + /* */ + /* <Description> */ + /* FreeType root glyph slot class structure. A glyph slot is a */ + /* container where individual glyphs can be loaded, be they in */ + /* outline or bitmap format. */ + /* */ + /* <Fields> */ + /* library :: A handle to the FreeType library instance */ + /* this slot belongs to. */ + /* */ + /* face :: A handle to the parent face object. */ + /* */ + /* next :: In some cases (like some font tools), several */ + /* glyph slots per face object can be a good */ + /* thing. As this is rare, the glyph slots are */ + /* listed through a direct, single-linked list */ + /* using its `next' field. */ + /* */ + /* generic :: A typeless pointer which is unused by the */ + /* FreeType library or any of its drivers. It */ + /* can be used by client applications to link */ + /* their own data to each glyph slot object. */ + /* */ + /* metrics :: The metrics of the last loaded glyph in the */ + /* slot. The returned values depend on the last */ + /* load flags (see the @FT_Load_Glyph API */ + /* function) and can be expressed either in 26.6 */ + /* fractional pixels or font units. */ + /* */ + /* Note that even when the glyph image is */ + /* transformed, the metrics are not. */ + /* */ + /* linearHoriAdvance :: The advance width of the unhinted glyph. */ + /* Its value is expressed in 16.16 fractional */ + /* pixels, unless @FT_LOAD_LINEAR_DESIGN is set */ + /* when loading the glyph. This field can be */ + /* important to perform correct WYSIWYG layout. */ + /* Only relevant for outline glyphs. */ + /* */ + /* linearVertAdvance :: The advance height of the unhinted glyph. */ + /* Its value is expressed in 16.16 fractional */ + /* pixels, unless @FT_LOAD_LINEAR_DESIGN is set */ + /* when loading the glyph. This field can be */ + /* important to perform correct WYSIWYG layout. */ + /* Only relevant for outline glyphs. */ + /* */ + /* advance :: This is the transformed advance width for the */ + /* glyph (in 26.6 fractional pixel format). */ + /* */ + /* format :: This field indicates the format of the image */ + /* contained in the glyph slot. Typically */ + /* @FT_GLYPH_FORMAT_BITMAP, */ + /* @FT_GLYPH_FORMAT_OUTLINE, or */ + /* @FT_GLYPH_FORMAT_COMPOSITE, but others are */ + /* possible. */ + /* */ + /* bitmap :: This field is used as a bitmap descriptor */ + /* when the slot format is */ + /* @FT_GLYPH_FORMAT_BITMAP. Note that the */ + /* address and content of the bitmap buffer can */ + /* change between calls of @FT_Load_Glyph and a */ + /* few other functions. */ + /* */ + /* bitmap_left :: This is the bitmap's left bearing expressed */ + /* in integer pixels. Of course, this is only */ + /* valid if the format is */ + /* @FT_GLYPH_FORMAT_BITMAP. */ + /* */ + /* bitmap_top :: This is the bitmap's top bearing expressed in */ + /* integer pixels. Remember that this is the */ + /* distance from the baseline to the top-most */ + /* glyph scanline, upwards y~coordinates being */ + /* *positive*. */ + /* */ + /* outline :: The outline descriptor for the current glyph */ + /* image if its format is */ + /* @FT_GLYPH_FORMAT_OUTLINE. Once a glyph is */ + /* loaded, `outline' can be transformed, */ + /* distorted, embolded, etc. However, it must */ + /* not be freed. */ + /* */ + /* num_subglyphs :: The number of subglyphs in a composite glyph. */ + /* This field is only valid for the composite */ + /* glyph format that should normally only be */ + /* loaded with the @FT_LOAD_NO_RECURSE flag. */ + /* For now this is internal to FreeType. */ + /* */ + /* subglyphs :: An array of subglyph descriptors for */ + /* composite glyphs. There are `num_subglyphs' */ + /* elements in there. Currently internal to */ + /* FreeType. */ + /* */ + /* control_data :: Certain font drivers can also return the */ + /* control data for a given glyph image (e.g. */ + /* TrueType bytecode, Type~1 charstrings, etc.). */ + /* This field is a pointer to such data. */ + /* */ + /* control_len :: This is the length in bytes of the control */ + /* data. */ + /* */ + /* other :: Really wicked formats can use this pointer to */ + /* present their own glyph image to client */ + /* applications. Note that the application */ + /* needs to know about the image format. */ + /* */ + /* lsb_delta :: The difference between hinted and unhinted */ + /* left side bearing while autohinting is */ + /* active. Zero otherwise. */ + /* */ + /* rsb_delta :: The difference between hinted and unhinted */ + /* right side bearing while autohinting is */ + /* active. Zero otherwise. */ + /* */ + /* <Note> */ + /* If @FT_Load_Glyph is called with default flags (see */ + /* @FT_LOAD_DEFAULT) the glyph image is loaded in the glyph slot in */ + /* its native format (e.g., an outline glyph for TrueType and Type~1 */ + /* formats). */ + /* */ + /* This image can later be converted into a bitmap by calling */ + /* @FT_Render_Glyph. This function finds the current renderer for */ + /* the native image's format, then invokes it. */ + /* */ + /* The renderer is in charge of transforming the native image through */ + /* the slot's face transformation fields, then converting it into a */ + /* bitmap that is returned in `slot->bitmap'. */ + /* */ + /* Note that `slot->bitmap_left' and `slot->bitmap_top' are also used */ + /* to specify the position of the bitmap relative to the current pen */ + /* position (e.g., coordinates (0,0) on the baseline). Of course, */ + /* `slot->format' is also changed to @FT_GLYPH_FORMAT_BITMAP. */ + /* */ + /* <Note> */ + /* Here a small pseudo code fragment which shows how to use */ + /* `lsb_delta' and `rsb_delta': */ + /* */ + /* { */ + /* FT_Pos origin_x = 0; */ + /* FT_Pos prev_rsb_delta = 0; */ + /* */ + /* */ + /* for all glyphs do */ + /* <compute kern between current and previous glyph and add it to */ + /* `origin_x'> */ + /* */ + /* <load glyph with `FT_Load_Glyph'> */ + /* */ + /* if ( prev_rsb_delta - face->glyph->lsb_delta >= 32 ) */ + /* origin_x -= 64; */ + /* else if ( prev_rsb_delta - face->glyph->lsb_delta < -32 ) */ + /* origin_x += 64; */ + /* */ + /* prev_rsb_delta = face->glyph->rsb_delta; */ + /* */ + /* <save glyph image, or render glyph, or ...> */ + /* */ + /* origin_x += face->glyph->advance.x; */ + /* endfor */ + /* } */ + /* */ + typedef struct FT_GlyphSlotRec_ + { + FT_Library library; + FT_Face face; + FT_GlyphSlot next; + FT_UInt reserved; /* retained for binary compatibility */ + FT_Generic generic; + + FT_Glyph_Metrics metrics; + FT_Fixed linearHoriAdvance; + FT_Fixed linearVertAdvance; + FT_Vector advance; + + FT_Glyph_Format format; + + FT_Bitmap bitmap; + FT_Int bitmap_left; + FT_Int bitmap_top; + + FT_Outline outline; + + FT_UInt num_subglyphs; + FT_SubGlyph subglyphs; + + void* control_data; + long control_len; + + FT_Pos lsb_delta; + FT_Pos rsb_delta; + + void* other; + + FT_Slot_Internal internal; + + } FT_GlyphSlotRec; + + + /*************************************************************************/ + /*************************************************************************/ + /* */ + /* F U N C T I O N S */ + /* */ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Init_FreeType */ + /* */ + /* <Description> */ + /* Initialize a new FreeType library object. The set of modules */ + /* that are registered by this function is determined at build time. */ + /* */ + /* <Output> */ + /* alibrary :: A handle to a new library object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Init_FreeType( FT_Library *alibrary ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_FreeType */ + /* */ + /* <Description> */ + /* Destroy a given FreeType library object and all of its children, */ + /* including resources, drivers, faces, sizes, etc. */ + /* */ + /* <Input> */ + /* library :: A handle to the target library object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Done_FreeType( FT_Library library ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_OPEN_XXX */ + /* */ + /* <Description> */ + /* A list of bit-field constants used within the `flags' field of the */ + /* @FT_Open_Args structure. */ + /* */ + /* <Values> */ + /* FT_OPEN_MEMORY :: This is a memory-based stream. */ + /* */ + /* FT_OPEN_STREAM :: Copy the stream from the `stream' field. */ + /* */ + /* FT_OPEN_PATHNAME :: Create a new input stream from a C~path */ + /* name. */ + /* */ + /* FT_OPEN_DRIVER :: Use the `driver' field. */ + /* */ + /* FT_OPEN_PARAMS :: Use the `num_params' and `params' fields. */ + /* */ + /* ft_open_memory :: Deprecated; use @FT_OPEN_MEMORY instead. */ + /* */ + /* ft_open_stream :: Deprecated; use @FT_OPEN_STREAM instead. */ + /* */ + /* ft_open_pathname :: Deprecated; use @FT_OPEN_PATHNAME instead. */ + /* */ + /* ft_open_driver :: Deprecated; use @FT_OPEN_DRIVER instead. */ + /* */ + /* ft_open_params :: Deprecated; use @FT_OPEN_PARAMS instead. */ + /* */ + /* <Note> */ + /* The `FT_OPEN_MEMORY', `FT_OPEN_STREAM', and `FT_OPEN_PATHNAME' */ + /* flags are mutually exclusive. */ + /* */ +#define FT_OPEN_MEMORY 0x1 +#define FT_OPEN_STREAM 0x2 +#define FT_OPEN_PATHNAME 0x4 +#define FT_OPEN_DRIVER 0x8 +#define FT_OPEN_PARAMS 0x10 + +#define ft_open_memory FT_OPEN_MEMORY /* deprecated */ +#define ft_open_stream FT_OPEN_STREAM /* deprecated */ +#define ft_open_pathname FT_OPEN_PATHNAME /* deprecated */ +#define ft_open_driver FT_OPEN_DRIVER /* deprecated */ +#define ft_open_params FT_OPEN_PARAMS /* deprecated */ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Parameter */ + /* */ + /* <Description> */ + /* A simple structure used to pass more or less generic parameters to */ + /* @FT_Open_Face. */ + /* */ + /* <Fields> */ + /* tag :: A four-byte identification tag. */ + /* */ + /* data :: A pointer to the parameter data. */ + /* */ + /* <Note> */ + /* The ID and function of parameters are driver-specific. */ + /* */ + typedef struct FT_Parameter_ + { + FT_ULong tag; + FT_Pointer data; + + } FT_Parameter; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Open_Args */ + /* */ + /* <Description> */ + /* A structure used to indicate how to open a new font file or */ + /* stream. A pointer to such a structure can be used as a parameter */ + /* for the functions @FT_Open_Face and @FT_Attach_Stream. */ + /* */ + /* <Fields> */ + /* flags :: A set of bit flags indicating how to use the */ + /* structure. */ + /* */ + /* memory_base :: The first byte of the file in memory. */ + /* */ + /* memory_size :: The size in bytes of the file in memory. */ + /* */ + /* pathname :: A pointer to an 8-bit file pathname. */ + /* */ + /* stream :: A handle to a source stream object. */ + /* */ + /* driver :: This field is exclusively used by @FT_Open_Face; */ + /* it simply specifies the font driver to use to open */ + /* the face. If set to~0, FreeType tries to load the */ + /* face with each one of the drivers in its list. */ + /* */ + /* num_params :: The number of extra parameters. */ + /* */ + /* params :: Extra parameters passed to the font driver when */ + /* opening a new face. */ + /* */ + /* <Note> */ + /* The stream type is determined by the contents of `flags' which */ + /* are tested in the following order by @FT_Open_Face: */ + /* */ + /* If the `FT_OPEN_MEMORY' bit is set, assume that this is a */ + /* memory file of `memory_size' bytes, located at `memory_address'. */ + /* The data are are not copied, and the client is responsible for */ + /* releasing and destroying them _after_ the corresponding call to */ + /* @FT_Done_Face. */ + /* */ + /* Otherwise, if the `FT_OPEN_STREAM' bit is set, assume that a */ + /* custom input stream `stream' is used. */ + /* */ + /* Otherwise, if the `FT_OPEN_PATHNAME' bit is set, assume that this */ + /* is a normal file and use `pathname' to open it. */ + /* */ + /* If the `FT_OPEN_DRIVER' bit is set, @FT_Open_Face only tries to */ + /* open the file with the driver whose handler is in `driver'. */ + /* */ + /* If the `FT_OPEN_PARAMS' bit is set, the parameters given by */ + /* `num_params' and `params' is used. They are ignored otherwise. */ + /* */ + /* Ideally, both the `pathname' and `params' fields should be tagged */ + /* as `const'; this is missing for API backwards compatibility. In */ + /* other words, applications should treat them as read-only. */ + /* */ + typedef struct FT_Open_Args_ + { + FT_UInt flags; + const FT_Byte* memory_base; + FT_Long memory_size; + FT_String* pathname; + FT_Stream stream; + FT_Module driver; + FT_Int num_params; + FT_Parameter* params; + + } FT_Open_Args; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Face */ + /* */ + /* <Description> */ + /* This function calls @FT_Open_Face to open a font by its pathname. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library resource. */ + /* */ + /* <Input> */ + /* pathname :: A path to the font file. */ + /* */ + /* face_index :: The index of the face within the font. The first */ + /* face has index~0. */ + /* */ + /* <Output> */ + /* aface :: A handle to a new face object. If `face_index' is */ + /* greater than or equal to zero, it must be non-NULL. */ + /* See @FT_Open_Face for more details. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Face( FT_Library library, + const char* filepathname, + FT_Long face_index, + FT_Face *aface ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_New_Memory_Face */ + /* */ + /* <Description> */ + /* This function calls @FT_Open_Face to open a font which has been */ + /* loaded into memory. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library resource. */ + /* */ + /* <Input> */ + /* file_base :: A pointer to the beginning of the font data. */ + /* */ + /* file_size :: The size of the memory chunk used by the font data. */ + /* */ + /* face_index :: The index of the face within the font. The first */ + /* face has index~0. */ + /* */ + /* <Output> */ + /* aface :: A handle to a new face object. If `face_index' is */ + /* greater than or equal to zero, it must be non-NULL. */ + /* See @FT_Open_Face for more details. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* You must not deallocate the memory before calling @FT_Done_Face. */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Memory_Face( FT_Library library, + const FT_Byte* file_base, + FT_Long file_size, + FT_Long face_index, + FT_Face *aface ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Open_Face */ + /* */ + /* <Description> */ + /* Create a face object from a given resource described by */ + /* @FT_Open_Args. */ + /* */ + /* <InOut> */ + /* library :: A handle to the library resource. */ + /* */ + /* <Input> */ + /* args :: A pointer to an `FT_Open_Args' structure which must */ + /* be filled by the caller. */ + /* */ + /* face_index :: The index of the face within the font. The first */ + /* face has index~0. */ + /* */ + /* <Output> */ + /* aface :: A handle to a new face object. If `face_index' is */ + /* greater than or equal to zero, it must be non-NULL. */ + /* See note below. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* Unlike FreeType 1.x, this function automatically creates a glyph */ + /* slot for the face object which can be accessed directly through */ + /* `face->glyph'. */ + /* */ + /* FT_Open_Face can be used to quickly check whether the font */ + /* format of a given font resource is supported by FreeType. If the */ + /* `face_index' field is negative, the function's return value is~0 */ + /* if the font format is recognized, or non-zero otherwise; */ + /* the function returns a more or less empty face handle in `*aface' */ + /* (if `aface' isn't NULL). The only useful field in this special */ + /* case is `face->num_faces' which gives the number of faces within */ + /* the font file. After examination, the returned @FT_Face structure */ + /* should be deallocated with a call to @FT_Done_Face. */ + /* */ + /* Each new face object created with this function also owns a */ + /* default @FT_Size object, accessible as `face->size'. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Open_Face( FT_Library library, + const FT_Open_Args* args, + FT_Long face_index, + FT_Face *aface ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Attach_File */ + /* */ + /* <Description> */ + /* This function calls @FT_Attach_Stream to attach a file. */ + /* */ + /* <InOut> */ + /* face :: The target face object. */ + /* */ + /* <Input> */ + /* filepathname :: The pathname. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Attach_File( FT_Face face, + const char* filepathname ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Attach_Stream */ + /* */ + /* <Description> */ + /* `Attach' data to a face object. Normally, this is used to read */ + /* additional information for the face object. For example, you can */ + /* attach an AFM file that comes with a Type~1 font to get the */ + /* kerning values and other metrics. */ + /* */ + /* <InOut> */ + /* face :: The target face object. */ + /* */ + /* <Input> */ + /* parameters :: A pointer to @FT_Open_Args which must be filled by */ + /* the caller. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The meaning of the `attach' (i.e., what really happens when the */ + /* new file is read) is not fixed by FreeType itself. It really */ + /* depends on the font format (and thus the font driver). */ + /* */ + /* Client applications are expected to know what they are doing */ + /* when invoking this function. Most drivers simply do not implement */ + /* file attachments. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Attach_Stream( FT_Face face, + FT_Open_Args* parameters ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_Face */ + /* */ + /* <Description> */ + /* Discard a given face object, as well as all of its child slots and */ + /* sizes. */ + /* */ + /* <Input> */ + /* face :: A handle to a target face object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Done_Face( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Select_Size */ + /* */ + /* <Description> */ + /* Select a bitmap strike. */ + /* */ + /* <InOut> */ + /* face :: A handle to a target face object. */ + /* */ + /* <Input> */ + /* strike_index :: The index of the bitmap strike in the */ + /* `available_sizes' field of @FT_FaceRec structure. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Select_Size( FT_Face face, + FT_Int strike_index ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Size_Request_Type */ + /* */ + /* <Description> */ + /* An enumeration type that lists the supported size request types. */ + /* */ + /* <Values> */ + /* FT_SIZE_REQUEST_TYPE_NOMINAL :: */ + /* The nominal size. The `units_per_EM' field of @FT_FaceRec is */ + /* used to determine both scaling values. */ + /* */ + /* FT_SIZE_REQUEST_TYPE_REAL_DIM :: */ + /* The real dimension. The sum of the the `Ascender' and (minus */ + /* of) the `Descender' fields of @FT_FaceRec are used to determine */ + /* both scaling values. */ + /* */ + /* FT_SIZE_REQUEST_TYPE_BBOX :: */ + /* The font bounding box. The width and height of the `bbox' field */ + /* of @FT_FaceRec are used to determine the horizontal and vertical */ + /* scaling value, respectively. */ + /* */ + /* FT_SIZE_REQUEST_TYPE_CELL :: */ + /* The `max_advance_width' field of @FT_FaceRec is used to */ + /* determine the horizontal scaling value; the vertical scaling */ + /* value is determined the same way as */ + /* @FT_SIZE_REQUEST_TYPE_REAL_DIM does. Finally, both scaling */ + /* values are set to the smaller one. This type is useful if you */ + /* want to specify the font size for, say, a window of a given */ + /* dimension and 80x24 cells. */ + /* */ + /* FT_SIZE_REQUEST_TYPE_SCALES :: */ + /* Specify the scaling values directly. */ + /* */ + /* <Note> */ + /* The above descriptions only apply to scalable formats. For bitmap */ + /* formats, the behaviour is up to the driver. */ + /* */ + /* See the note section of @FT_Size_Metrics if you wonder how size */ + /* requesting relates to scaling values. */ + /* */ + typedef enum FT_Size_Request_Type_ + { + FT_SIZE_REQUEST_TYPE_NOMINAL, + FT_SIZE_REQUEST_TYPE_REAL_DIM, + FT_SIZE_REQUEST_TYPE_BBOX, + FT_SIZE_REQUEST_TYPE_CELL, + FT_SIZE_REQUEST_TYPE_SCALES, + + FT_SIZE_REQUEST_TYPE_MAX + + } FT_Size_Request_Type; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Size_RequestRec */ + /* */ + /* <Description> */ + /* A structure used to model a size request. */ + /* */ + /* <Fields> */ + /* type :: See @FT_Size_Request_Type. */ + /* */ + /* width :: The desired width. */ + /* */ + /* height :: The desired height. */ + /* */ + /* horiResolution :: The horizontal resolution. If set to zero, */ + /* `width' is treated as a 26.6 fractional pixel */ + /* value. */ + /* */ + /* vertResolution :: The vertical resolution. If set to zero, */ + /* `height' is treated as a 26.6 fractional pixel */ + /* value. */ + /* */ + /* <Note> */ + /* If `width' is zero, then the horizontal scaling value is set equal */ + /* to the vertical scaling value, and vice versa. */ + /* */ + typedef struct FT_Size_RequestRec_ + { + FT_Size_Request_Type type; + FT_Long width; + FT_Long height; + FT_UInt horiResolution; + FT_UInt vertResolution; + + } FT_Size_RequestRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Size_Request */ + /* */ + /* <Description> */ + /* A handle to a size request structure. */ + /* */ + typedef struct FT_Size_RequestRec_ *FT_Size_Request; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Request_Size */ + /* */ + /* <Description> */ + /* Resize the scale of the active @FT_Size object in a face. */ + /* */ + /* <InOut> */ + /* face :: A handle to a target face object. */ + /* */ + /* <Input> */ + /* req :: A pointer to a @FT_Size_RequestRec. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* Although drivers may select the bitmap strike matching the */ + /* request, you should not rely on this if you intend to select a */ + /* particular bitmap strike. Use @FT_Select_Size instead in that */ + /* case. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Request_Size( FT_Face face, + FT_Size_Request req ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Char_Size */ + /* */ + /* <Description> */ + /* This function calls @FT_Request_Size to request the nominal size */ + /* (in points). */ + /* */ + /* <InOut> */ + /* face :: A handle to a target face object. */ + /* */ + /* <Input> */ + /* char_width :: The nominal width, in 26.6 fractional points. */ + /* */ + /* char_height :: The nominal height, in 26.6 fractional points. */ + /* */ + /* horz_resolution :: The horizontal resolution in dpi. */ + /* */ + /* vert_resolution :: The vertical resolution in dpi. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* If either the character width or height is zero, it is set equal */ + /* to the other value. */ + /* */ + /* If either the horizontal or vertical resolution is zero, it is set */ + /* equal to the other value. */ + /* */ + /* A character width or height smaller than 1pt is set to 1pt; if */ + /* both resolution values are zero, they are set to 72dpi. */ + /* */ + /* Don't use this function if you are using the FreeType cache API. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_Char_Size( FT_Face face, + FT_F26Dot6 char_width, + FT_F26Dot6 char_height, + FT_UInt horz_resolution, + FT_UInt vert_resolution ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Pixel_Sizes */ + /* */ + /* <Description> */ + /* This function calls @FT_Request_Size to request the nominal size */ + /* (in pixels). */ + /* */ + /* <InOut> */ + /* face :: A handle to the target face object. */ + /* */ + /* <Input> */ + /* pixel_width :: The nominal width, in pixels. */ + /* */ + /* pixel_height :: The nominal height, in pixels. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_Pixel_Sizes( FT_Face face, + FT_UInt pixel_width, + FT_UInt pixel_height ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Load_Glyph */ + /* */ + /* <Description> */ + /* A function used to load a single glyph into the glyph slot of a */ + /* face object. */ + /* */ + /* <InOut> */ + /* face :: A handle to the target face object where the glyph */ + /* is loaded. */ + /* */ + /* <Input> */ + /* glyph_index :: The index of the glyph in the font file. For */ + /* CID-keyed fonts (either in PS or in CFF format) */ + /* this argument specifies the CID value. */ + /* */ + /* load_flags :: A flag indicating what to load for this glyph. The */ + /* @FT_LOAD_XXX constants can be used to control the */ + /* glyph loading process (e.g., whether the outline */ + /* should be scaled, whether to load bitmaps or not, */ + /* whether to hint the outline, etc). */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The loaded glyph may be transformed. See @FT_Set_Transform for */ + /* the details. */ + /* */ + /* For subsetted CID-keyed fonts, `FT_Err_Invalid_Argument' is */ + /* returned for invalid CID values (this is, for CID values which */ + /* don't have a corresponding glyph in the font). See the discussion */ + /* of the @FT_FACE_FLAG_CID_KEYED flag for more details. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Load_Glyph( FT_Face face, + FT_UInt glyph_index, + FT_Int32 load_flags ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Load_Char */ + /* */ + /* <Description> */ + /* A function used to load a single glyph into the glyph slot of a */ + /* face object, according to its character code. */ + /* */ + /* <InOut> */ + /* face :: A handle to a target face object where the glyph */ + /* is loaded. */ + /* */ + /* <Input> */ + /* char_code :: The glyph's character code, according to the */ + /* current charmap used in the face. */ + /* */ + /* load_flags :: A flag indicating what to load for this glyph. The */ + /* @FT_LOAD_XXX constants can be used to control the */ + /* glyph loading process (e.g., whether the outline */ + /* should be scaled, whether to load bitmaps or not, */ + /* whether to hint the outline, etc). */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* This function simply calls @FT_Get_Char_Index and @FT_Load_Glyph. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Load_Char( FT_Face face, + FT_ULong char_code, + FT_Int32 load_flags ); + + + /************************************************************************* + * + * @enum: + * FT_LOAD_XXX + * + * @description: + * A list of bit-field constants used with @FT_Load_Glyph to indicate + * what kind of operations to perform during glyph loading. + * + * @values: + * FT_LOAD_DEFAULT :: + * Corresponding to~0, this value is used as the default glyph load + * operation. In this case, the following happens: + * + * 1. FreeType looks for a bitmap for the glyph corresponding to the + * face's current size. If one is found, the function returns. + * The bitmap data can be accessed from the glyph slot (see note + * below). + * + * 2. If no embedded bitmap is searched or found, FreeType looks for a + * scalable outline. If one is found, it is loaded from the font + * file, scaled to device pixels, then `hinted' to the pixel grid + * in order to optimize it. The outline data can be accessed from + * the glyph slot (see note below). + * + * Note that by default, the glyph loader doesn't render outlines into + * bitmaps. The following flags are used to modify this default + * behaviour to more specific and useful cases. + * + * FT_LOAD_NO_SCALE :: + * Don't scale the outline glyph loaded, but keep it in font units. + * + * This flag implies @FT_LOAD_NO_HINTING and @FT_LOAD_NO_BITMAP, and + * unsets @FT_LOAD_RENDER. + * + * FT_LOAD_NO_HINTING :: + * Disable hinting. This generally generates `blurrier' bitmap glyph + * when the glyph is rendered in any of the anti-aliased modes. See + * also the note below. + * + * This flag is implied by @FT_LOAD_NO_SCALE. + * + * FT_LOAD_RENDER :: + * Call @FT_Render_Glyph after the glyph is loaded. By default, the + * glyph is rendered in @FT_RENDER_MODE_NORMAL mode. This can be + * overridden by @FT_LOAD_TARGET_XXX or @FT_LOAD_MONOCHROME. + * + * This flag is unset by @FT_LOAD_NO_SCALE. + * + * FT_LOAD_NO_BITMAP :: + * Ignore bitmap strikes when loading. Bitmap-only fonts ignore this + * flag. + * + * @FT_LOAD_NO_SCALE always sets this flag. + * + * FT_LOAD_VERTICAL_LAYOUT :: + * Load the glyph for vertical text layout. _Don't_ use it as it is + * problematic currently. + * + * FT_LOAD_FORCE_AUTOHINT :: + * Indicates that the auto-hinter is preferred over the font's native + * hinter. See also the note below. + * + * FT_LOAD_CROP_BITMAP :: + * Indicates that the font driver should crop the loaded bitmap glyph + * (i.e., remove all space around its black bits). Not all drivers + * implement this. + * + * FT_LOAD_PEDANTIC :: + * Indicates that the font driver should perform pedantic verifications + * during glyph loading. This is mostly used to detect broken glyphs + * in fonts. By default, FreeType tries to handle broken fonts also. + * + * FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH :: + * Indicates that the font driver should ignore the global advance + * width defined in the font. By default, that value is used as the + * advance width for all glyphs when the face has + * @FT_FACE_FLAG_FIXED_WIDTH set. + * + * This flag exists for historical reasons (to support buggy CJK + * fonts). + * + * FT_LOAD_NO_RECURSE :: + * This flag is only used internally. It merely indicates that the + * font driver should not load composite glyphs recursively. Instead, + * it should set the `num_subglyph' and `subglyphs' values of the + * glyph slot accordingly, and set `glyph->format' to + * @FT_GLYPH_FORMAT_COMPOSITE. + * + * The description of sub-glyphs is not available to client + * applications for now. + * + * This flag implies @FT_LOAD_NO_SCALE and @FT_LOAD_IGNORE_TRANSFORM. + * + * FT_LOAD_IGNORE_TRANSFORM :: + * Indicates that the transform matrix set by @FT_Set_Transform should + * be ignored. + * + * FT_LOAD_MONOCHROME :: + * This flag is used with @FT_LOAD_RENDER to indicate that you want to + * render an outline glyph to a 1-bit monochrome bitmap glyph, with + * 8~pixels packed into each byte of the bitmap data. + * + * Note that this has no effect on the hinting algorithm used. You + * should rather use @FT_LOAD_TARGET_MONO so that the + * monochrome-optimized hinting algorithm is used. + * + * FT_LOAD_LINEAR_DESIGN :: + * Indicates that the `linearHoriAdvance' and `linearVertAdvance' + * fields of @FT_GlyphSlotRec should be kept in font units. See + * @FT_GlyphSlotRec for details. + * + * FT_LOAD_NO_AUTOHINT :: + * Disable auto-hinter. See also the note below. + * + * @note: + * By default, hinting is enabled and the font's native hinter (see + * @FT_FACE_FLAG_HINTER) is preferred over the auto-hinter. You can + * disable hinting by setting @FT_LOAD_NO_HINTING or change the + * precedence by setting @FT_LOAD_FORCE_AUTOHINT. You can also set + * @FT_LOAD_NO_AUTOHINT in case you don't want the auto-hinter to be + * used at all. + * + * See the description of @FT_FACE_FLAG_TRICKY for a special exception + * (affecting only a handful of Asian fonts). + * + * Besides deciding which hinter to use, you can also decide which + * hinting algorithm to use. See @FT_LOAD_TARGET_XXX for details. + * + */ +#define FT_LOAD_DEFAULT 0x0 +#define FT_LOAD_NO_SCALE 0x1 +#define FT_LOAD_NO_HINTING 0x2 +#define FT_LOAD_RENDER 0x4 +#define FT_LOAD_NO_BITMAP 0x8 +#define FT_LOAD_VERTICAL_LAYOUT 0x10 +#define FT_LOAD_FORCE_AUTOHINT 0x20 +#define FT_LOAD_CROP_BITMAP 0x40 +#define FT_LOAD_PEDANTIC 0x80 +#define FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH 0x200 +#define FT_LOAD_NO_RECURSE 0x400 +#define FT_LOAD_IGNORE_TRANSFORM 0x800 +#define FT_LOAD_MONOCHROME 0x1000 +#define FT_LOAD_LINEAR_DESIGN 0x2000 +#define FT_LOAD_NO_AUTOHINT 0x8000U + + /* */ + + /* used internally only by certain font drivers! */ +#define FT_LOAD_ADVANCE_ONLY 0x100 +#define FT_LOAD_SBITS_ONLY 0x4000 + + + /************************************************************************** + * + * @enum: + * FT_LOAD_TARGET_XXX + * + * @description: + * A list of values that are used to select a specific hinting algorithm + * to use by the hinter. You should OR one of these values to your + * `load_flags' when calling @FT_Load_Glyph. + * + * Note that font's native hinters may ignore the hinting algorithm you + * have specified (e.g., the TrueType bytecode interpreter). You can set + * @FT_LOAD_FORCE_AUTOHINT to ensure that the auto-hinter is used. + * + * Also note that @FT_LOAD_TARGET_LIGHT is an exception, in that it + * always implies @FT_LOAD_FORCE_AUTOHINT. + * + * @values: + * FT_LOAD_TARGET_NORMAL :: + * This corresponds to the default hinting algorithm, optimized for + * standard gray-level rendering. For monochrome output, use + * @FT_LOAD_TARGET_MONO instead. + * + * FT_LOAD_TARGET_LIGHT :: + * A lighter hinting algorithm for non-monochrome modes. Many + * generated glyphs are more fuzzy but better resemble its original + * shape. A bit like rendering on Mac OS~X. + * + * As a special exception, this target implies @FT_LOAD_FORCE_AUTOHINT. + * + * FT_LOAD_TARGET_MONO :: + * Strong hinting algorithm that should only be used for monochrome + * output. The result is probably unpleasant if the glyph is rendered + * in non-monochrome modes. + * + * FT_LOAD_TARGET_LCD :: + * A variant of @FT_LOAD_TARGET_NORMAL optimized for horizontally + * decimated LCD displays. + * + * FT_LOAD_TARGET_LCD_V :: + * A variant of @FT_LOAD_TARGET_NORMAL optimized for vertically + * decimated LCD displays. + * + * @note: + * You should use only _one_ of the FT_LOAD_TARGET_XXX values in your + * `load_flags'. They can't be ORed. + * + * If @FT_LOAD_RENDER is also set, the glyph is rendered in the + * corresponding mode (i.e., the mode which matches the used algorithm + * best) unless @FT_LOAD_MONOCHROME is set. + * + * You can use a hinting algorithm that doesn't correspond to the same + * rendering mode. As an example, it is possible to use the `light' + * hinting algorithm and have the results rendered in horizontal LCD + * pixel mode, with code like + * + * { + * FT_Load_Glyph( face, glyph_index, + * load_flags | FT_LOAD_TARGET_LIGHT ); + * + * FT_Render_Glyph( face->glyph, FT_RENDER_MODE_LCD ); + * } + * + */ +#define FT_LOAD_TARGET_( x ) ( (FT_Int32)( (x) & 15 ) << 16 ) + +#define FT_LOAD_TARGET_NORMAL FT_LOAD_TARGET_( FT_RENDER_MODE_NORMAL ) +#define FT_LOAD_TARGET_LIGHT FT_LOAD_TARGET_( FT_RENDER_MODE_LIGHT ) +#define FT_LOAD_TARGET_MONO FT_LOAD_TARGET_( FT_RENDER_MODE_MONO ) +#define FT_LOAD_TARGET_LCD FT_LOAD_TARGET_( FT_RENDER_MODE_LCD ) +#define FT_LOAD_TARGET_LCD_V FT_LOAD_TARGET_( FT_RENDER_MODE_LCD_V ) + + + /************************************************************************** + * + * @macro: + * FT_LOAD_TARGET_MODE + * + * @description: + * Return the @FT_Render_Mode corresponding to a given + * @FT_LOAD_TARGET_XXX value. + * + */ +#define FT_LOAD_TARGET_MODE( x ) ( (FT_Render_Mode)( ( (x) >> 16 ) & 15 ) ) + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Transform */ + /* */ + /* <Description> */ + /* A function used to set the transformation that is applied to glyph */ + /* images when they are loaded into a glyph slot through */ + /* @FT_Load_Glyph. */ + /* */ + /* <InOut> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Input> */ + /* matrix :: A pointer to the transformation's 2x2 matrix. Use~0 for */ + /* the identity matrix. */ + /* delta :: A pointer to the translation vector. Use~0 for the null */ + /* vector. */ + /* */ + /* <Note> */ + /* The transformation is only applied to scalable image formats after */ + /* the glyph has been loaded. It means that hinting is unaltered by */ + /* the transformation and is performed on the character size given in */ + /* the last call to @FT_Set_Char_Size or @FT_Set_Pixel_Sizes. */ + /* */ + /* Note that this also transforms the `face.glyph.advance' field, but */ + /* *not* the values in `face.glyph.metrics'. */ + /* */ + FT_EXPORT( void ) + FT_Set_Transform( FT_Face face, + FT_Matrix* matrix, + FT_Vector* delta ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Render_Mode */ + /* */ + /* <Description> */ + /* An enumeration type that lists the render modes supported by */ + /* FreeType~2. Each mode corresponds to a specific type of scanline */ + /* conversion performed on the outline. */ + /* */ + /* For bitmap fonts and embedded bitmaps the `bitmap->pixel_mode' */ + /* field in the @FT_GlyphSlotRec structure gives the format of the */ + /* returned bitmap. */ + /* */ + /* All modes except @FT_RENDER_MODE_MONO use 256 levels of opacity. */ + /* */ + /* <Values> */ + /* FT_RENDER_MODE_NORMAL :: */ + /* This is the default render mode; it corresponds to 8-bit */ + /* anti-aliased bitmaps. */ + /* */ + /* FT_RENDER_MODE_LIGHT :: */ + /* This is equivalent to @FT_RENDER_MODE_NORMAL. It is only */ + /* defined as a separate value because render modes are also used */ + /* indirectly to define hinting algorithm selectors. See */ + /* @FT_LOAD_TARGET_XXX for details. */ + /* */ + /* FT_RENDER_MODE_MONO :: */ + /* This mode corresponds to 1-bit bitmaps (with 2~levels of */ + /* opacity). */ + /* */ + /* FT_RENDER_MODE_LCD :: */ + /* This mode corresponds to horizontal RGB and BGR sub-pixel */ + /* displays like LCD screens. It produces 8-bit bitmaps that are */ + /* 3~times the width of the original glyph outline in pixels, and */ + /* which use the @FT_PIXEL_MODE_LCD mode. */ + /* */ + /* FT_RENDER_MODE_LCD_V :: */ + /* This mode corresponds to vertical RGB and BGR sub-pixel displays */ + /* (like PDA screens, rotated LCD displays, etc.). It produces */ + /* 8-bit bitmaps that are 3~times the height of the original */ + /* glyph outline in pixels and use the @FT_PIXEL_MODE_LCD_V mode. */ + /* */ + /* <Note> */ + /* The LCD-optimized glyph bitmaps produced by FT_Render_Glyph can be */ + /* filtered to reduce color-fringes by using @FT_Library_SetLcdFilter */ + /* (not active in the default builds). It is up to the caller to */ + /* either call @FT_Library_SetLcdFilter (if available) or do the */ + /* filtering itself. */ + /* */ + /* The selected render mode only affects vector glyphs of a font. */ + /* Embedded bitmaps often have a different pixel mode like */ + /* @FT_PIXEL_MODE_MONO. You can use @FT_Bitmap_Convert to transform */ + /* them into 8-bit pixmaps. */ + /* */ + typedef enum FT_Render_Mode_ + { + FT_RENDER_MODE_NORMAL = 0, + FT_RENDER_MODE_LIGHT, + FT_RENDER_MODE_MONO, + FT_RENDER_MODE_LCD, + FT_RENDER_MODE_LCD_V, + + FT_RENDER_MODE_MAX + + } FT_Render_Mode; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* ft_render_mode_xxx */ + /* */ + /* <Description> */ + /* These constants are deprecated. Use the corresponding */ + /* @FT_Render_Mode values instead. */ + /* */ + /* <Values> */ + /* ft_render_mode_normal :: see @FT_RENDER_MODE_NORMAL */ + /* ft_render_mode_mono :: see @FT_RENDER_MODE_MONO */ + /* */ +#define ft_render_mode_normal FT_RENDER_MODE_NORMAL +#define ft_render_mode_mono FT_RENDER_MODE_MONO + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Render_Glyph */ + /* */ + /* <Description> */ + /* Convert a given glyph image to a bitmap. It does so by inspecting */ + /* the glyph image format, finding the relevant renderer, and */ + /* invoking it. */ + /* */ + /* <InOut> */ + /* slot :: A handle to the glyph slot containing the image to */ + /* convert. */ + /* */ + /* <Input> */ + /* render_mode :: This is the render mode used to render the glyph */ + /* image into a bitmap. See @FT_Render_Mode for a */ + /* list of possible values. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Render_Glyph( FT_GlyphSlot slot, + FT_Render_Mode render_mode ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Kerning_Mode */ + /* */ + /* <Description> */ + /* An enumeration used to specify which kerning values to return in */ + /* @FT_Get_Kerning. */ + /* */ + /* <Values> */ + /* FT_KERNING_DEFAULT :: Return scaled and grid-fitted kerning */ + /* distances (value is~0). */ + /* */ + /* FT_KERNING_UNFITTED :: Return scaled but un-grid-fitted kerning */ + /* distances. */ + /* */ + /* FT_KERNING_UNSCALED :: Return the kerning vector in original font */ + /* units. */ + /* */ + typedef enum FT_Kerning_Mode_ + { + FT_KERNING_DEFAULT = 0, + FT_KERNING_UNFITTED, + FT_KERNING_UNSCALED + + } FT_Kerning_Mode; + + + /*************************************************************************/ + /* */ + /* <Const> */ + /* ft_kerning_default */ + /* */ + /* <Description> */ + /* This constant is deprecated. Please use @FT_KERNING_DEFAULT */ + /* instead. */ + /* */ +#define ft_kerning_default FT_KERNING_DEFAULT + + + /*************************************************************************/ + /* */ + /* <Const> */ + /* ft_kerning_unfitted */ + /* */ + /* <Description> */ + /* This constant is deprecated. Please use @FT_KERNING_UNFITTED */ + /* instead. */ + /* */ +#define ft_kerning_unfitted FT_KERNING_UNFITTED + + + /*************************************************************************/ + /* */ + /* <Const> */ + /* ft_kerning_unscaled */ + /* */ + /* <Description> */ + /* This constant is deprecated. Please use @FT_KERNING_UNSCALED */ + /* instead. */ + /* */ +#define ft_kerning_unscaled FT_KERNING_UNSCALED + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Kerning */ + /* */ + /* <Description> */ + /* Return the kerning vector between two glyphs of a same face. */ + /* */ + /* <Input> */ + /* face :: A handle to a source face object. */ + /* */ + /* left_glyph :: The index of the left glyph in the kern pair. */ + /* */ + /* right_glyph :: The index of the right glyph in the kern pair. */ + /* */ + /* kern_mode :: See @FT_Kerning_Mode for more information. */ + /* Determines the scale and dimension of the returned */ + /* kerning vector. */ + /* */ + /* <Output> */ + /* akerning :: The kerning vector. This is either in font units */ + /* or in pixels (26.6 format) for scalable formats, */ + /* and in pixels for fixed-sizes formats. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* Only horizontal layouts (left-to-right & right-to-left) are */ + /* supported by this method. Other layouts, or more sophisticated */ + /* kernings, are out of the scope of this API function -- they can be */ + /* implemented through format-specific interfaces. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Kerning( FT_Face face, + FT_UInt left_glyph, + FT_UInt right_glyph, + FT_UInt kern_mode, + FT_Vector *akerning ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Track_Kerning */ + /* */ + /* <Description> */ + /* Return the track kerning for a given face object at a given size. */ + /* */ + /* <Input> */ + /* face :: A handle to a source face object. */ + /* */ + /* point_size :: The point size in 16.16 fractional points. */ + /* */ + /* degree :: The degree of tightness. */ + /* */ + /* <Output> */ + /* akerning :: The kerning in 16.16 fractional points. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Track_Kerning( FT_Face face, + FT_Fixed point_size, + FT_Int degree, + FT_Fixed* akerning ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Glyph_Name */ + /* */ + /* <Description> */ + /* Retrieve the ASCII name of a given glyph in a face. This only */ + /* works for those faces where @FT_HAS_GLYPH_NAMES(face) returns~1. */ + /* */ + /* <Input> */ + /* face :: A handle to a source face object. */ + /* */ + /* glyph_index :: The glyph index. */ + /* */ + /* buffer_max :: The maximal number of bytes available in the */ + /* buffer. */ + /* */ + /* <Output> */ + /* buffer :: A pointer to a target buffer where the name is */ + /* copied to. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* An error is returned if the face doesn't provide glyph names or if */ + /* the glyph index is invalid. In all cases of failure, the first */ + /* byte of `buffer' is set to~0 to indicate an empty name. */ + /* */ + /* The glyph name is truncated to fit within the buffer if it is too */ + /* long. The returned string is always zero-terminated. */ + /* */ + /* This function is not compiled within the library if the config */ + /* macro `FT_CONFIG_OPTION_NO_GLYPH_NAMES' is defined in */ + /* `include/freetype/config/ftoptions.h'. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Glyph_Name( FT_Face face, + FT_UInt glyph_index, + FT_Pointer buffer, + FT_UInt buffer_max ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Postscript_Name */ + /* */ + /* <Description> */ + /* Retrieve the ASCII PostScript name of a given face, if available. */ + /* This only works with PostScript and TrueType fonts. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Return> */ + /* A pointer to the face's PostScript name. NULL if unavailable. */ + /* */ + /* <Note> */ + /* The returned pointer is owned by the face and is destroyed with */ + /* it. */ + /* */ + FT_EXPORT( const char* ) + FT_Get_Postscript_Name( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Select_Charmap */ + /* */ + /* <Description> */ + /* Select a given charmap by its encoding tag (as listed in */ + /* `freetype.h'). */ + /* */ + /* <InOut> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Input> */ + /* encoding :: A handle to the selected encoding. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* This function returns an error if no charmap in the face */ + /* corresponds to the encoding queried here. */ + /* */ + /* Because many fonts contain more than a single cmap for Unicode */ + /* encoding, this function has some special code to select the one */ + /* which covers Unicode best (`best' in the sense that a UCS-4 cmap */ + /* is preferred to a UCS-2 cmap). It is thus preferable to */ + /* @FT_Set_Charmap in this case. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Select_Charmap( FT_Face face, + FT_Encoding encoding ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Set_Charmap */ + /* */ + /* <Description> */ + /* Select a given charmap for character code to glyph index mapping. */ + /* */ + /* <InOut> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Input> */ + /* charmap :: A handle to the selected charmap. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* This function returns an error if the charmap is not part of */ + /* the face (i.e., if it is not listed in the `face->charmaps' */ + /* table). */ + /* */ + /* It also fails if a type~14 charmap is selected. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Set_Charmap( FT_Face face, + FT_CharMap charmap ); + + + /************************************************************************* + * + * @function: + * FT_Get_Charmap_Index + * + * @description: + * Retrieve index of a given charmap. + * + * @input: + * charmap :: + * A handle to a charmap. + * + * @return: + * The index into the array of character maps within the face to which + * `charmap' belongs. + * + */ + FT_EXPORT( FT_Int ) + FT_Get_Charmap_Index( FT_CharMap charmap ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Char_Index */ + /* */ + /* <Description> */ + /* Return the glyph index of a given character code. This function */ + /* uses a charmap object to do the mapping. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* charcode :: The character code. */ + /* */ + /* <Return> */ + /* The glyph index. 0~means `undefined character code'. */ + /* */ + /* <Note> */ + /* If you use FreeType to manipulate the contents of font files */ + /* directly, be aware that the glyph index returned by this function */ + /* doesn't always correspond to the internal indices used within */ + /* the file. This is done to ensure that value~0 always corresponds */ + /* to the `missing glyph'. */ + /* */ + FT_EXPORT( FT_UInt ) + FT_Get_Char_Index( FT_Face face, + FT_ULong charcode ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_First_Char */ + /* */ + /* <Description> */ + /* This function is used to return the first character code in the */ + /* current charmap of a given face. It also returns the */ + /* corresponding glyph index. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Output> */ + /* agindex :: Glyph index of first character code. 0~if charmap is */ + /* empty. */ + /* */ + /* <Return> */ + /* The charmap's first character code. */ + /* */ + /* <Note> */ + /* You should use this function with @FT_Get_Next_Char to be able to */ + /* parse all character codes available in a given charmap. The code */ + /* should look like this: */ + /* */ + /* { */ + /* FT_ULong charcode; */ + /* FT_UInt gindex; */ + /* */ + /* */ + /* charcode = FT_Get_First_Char( face, &gindex ); */ + /* while ( gindex != 0 ) */ + /* { */ + /* ... do something with (charcode,gindex) pair ... */ + /* */ + /* charcode = FT_Get_Next_Char( face, charcode, &gindex ); */ + /* } */ + /* } */ + /* */ + /* Note that `*agindex' is set to~0 if the charmap is empty. The */ + /* result itself can be~0 in two cases: if the charmap is empty or */ + /* if the value~0 is the first valid character code. */ + /* */ + FT_EXPORT( FT_ULong ) + FT_Get_First_Char( FT_Face face, + FT_UInt *agindex ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Next_Char */ + /* */ + /* <Description> */ + /* This function is used to return the next character code in the */ + /* current charmap of a given face following the value `char_code', */ + /* as well as the corresponding glyph index. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* char_code :: The starting character code. */ + /* */ + /* <Output> */ + /* agindex :: Glyph index of next character code. 0~if charmap */ + /* is empty. */ + /* */ + /* <Return> */ + /* The charmap's next character code. */ + /* */ + /* <Note> */ + /* You should use this function with @FT_Get_First_Char to walk */ + /* over all character codes available in a given charmap. See the */ + /* note for this function for a simple code example. */ + /* */ + /* Note that `*agindex' is set to~0 when there are no more codes in */ + /* the charmap. */ + /* */ + FT_EXPORT( FT_ULong ) + FT_Get_Next_Char( FT_Face face, + FT_ULong char_code, + FT_UInt *agindex ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Name_Index */ + /* */ + /* <Description> */ + /* Return the glyph index of a given glyph name. This function uses */ + /* driver specific objects to do the translation. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* glyph_name :: The glyph name. */ + /* */ + /* <Return> */ + /* The glyph index. 0~means `undefined character code'. */ + /* */ + FT_EXPORT( FT_UInt ) + FT_Get_Name_Index( FT_Face face, + FT_String* glyph_name ); + + + /************************************************************************* + * + * @macro: + * FT_SUBGLYPH_FLAG_XXX + * + * @description: + * A list of constants used to describe subglyphs. Please refer to the + * TrueType specification for the meaning of the various flags. + * + * @values: + * FT_SUBGLYPH_FLAG_ARGS_ARE_WORDS :: + * FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES :: + * FT_SUBGLYPH_FLAG_ROUND_XY_TO_GRID :: + * FT_SUBGLYPH_FLAG_SCALE :: + * FT_SUBGLYPH_FLAG_XY_SCALE :: + * FT_SUBGLYPH_FLAG_2X2 :: + * FT_SUBGLYPH_FLAG_USE_MY_METRICS :: + * + */ +#define FT_SUBGLYPH_FLAG_ARGS_ARE_WORDS 1 +#define FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES 2 +#define FT_SUBGLYPH_FLAG_ROUND_XY_TO_GRID 4 +#define FT_SUBGLYPH_FLAG_SCALE 8 +#define FT_SUBGLYPH_FLAG_XY_SCALE 0x40 +#define FT_SUBGLYPH_FLAG_2X2 0x80 +#define FT_SUBGLYPH_FLAG_USE_MY_METRICS 0x200 + + + /************************************************************************* + * + * @func: + * FT_Get_SubGlyph_Info + * + * @description: + * Retrieve a description of a given subglyph. Only use it if + * `glyph->format' is @FT_GLYPH_FORMAT_COMPOSITE; an error is + * returned otherwise. + * + * @input: + * glyph :: + * The source glyph slot. + * + * sub_index :: + * The index of the subglyph. Must be less than + * `glyph->num_subglyphs'. + * + * @output: + * p_index :: + * The glyph index of the subglyph. + * + * p_flags :: + * The subglyph flags, see @FT_SUBGLYPH_FLAG_XXX. + * + * p_arg1 :: + * The subglyph's first argument (if any). + * + * p_arg2 :: + * The subglyph's second argument (if any). + * + * p_transform :: + * The subglyph transformation (if any). + * + * @return: + * FreeType error code. 0~means success. + * + * @note: + * The values of `*p_arg1', `*p_arg2', and `*p_transform' must be + * interpreted depending on the flags returned in `*p_flags'. See the + * TrueType specification for details. + * + */ + FT_EXPORT( FT_Error ) + FT_Get_SubGlyph_Info( FT_GlyphSlot glyph, + FT_UInt sub_index, + FT_Int *p_index, + FT_UInt *p_flags, + FT_Int *p_arg1, + FT_Int *p_arg2, + FT_Matrix *p_transform ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_FSTYPE_XXX */ + /* */ + /* <Description> */ + /* A list of bit flags used in the `fsType' field of the OS/2 table */ + /* in a TrueType or OpenType font and the `FSType' entry in a */ + /* PostScript font. These bit flags are returned by */ + /* @FT_Get_FSType_Flags; they inform client applications of embedding */ + /* and subsetting restrictions associated with a font. */ + /* */ + /* See http://www.adobe.com/devnet/acrobat/pdfs/FontPolicies.pdf for */ + /* more details. */ + /* */ + /* <Values> */ + /* FT_FSTYPE_INSTALLABLE_EMBEDDING :: */ + /* Fonts with no fsType bit set may be embedded and permanently */ + /* installed on the remote system by an application. */ + /* */ + /* FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING :: */ + /* Fonts that have only this bit set must not be modified, embedded */ + /* or exchanged in any manner without first obtaining permission of */ + /* the font software copyright owner. */ + /* */ + /* FT_FSTYPE_PREVIEW_AND_PRINT_EMBEDDING :: */ + /* If this bit is set, the font may be embedded and temporarily */ + /* loaded on the remote system. Documents containing Preview & */ + /* Print fonts must be opened `read-only'; no edits can be applied */ + /* to the document. */ + /* */ + /* FT_FSTYPE_EDITABLE_EMBEDDING :: */ + /* If this bit is set, the font may be embedded but must only be */ + /* installed temporarily on other systems. In contrast to Preview */ + /* & Print fonts, documents containing editable fonts may be opened */ + /* for reading, editing is permitted, and changes may be saved. */ + /* */ + /* FT_FSTYPE_NO_SUBSETTING :: */ + /* If this bit is set, the font may not be subsetted prior to */ + /* embedding. */ + /* */ + /* FT_FSTYPE_BITMAP_EMBEDDING_ONLY :: */ + /* If this bit is set, only bitmaps contained in the font may be */ + /* embedded; no outline data may be embedded. If there are no */ + /* bitmaps available in the font, then the font is unembeddable. */ + /* */ + /* <Note> */ + /* While the fsType flags can indicate that a font may be embedded, a */ + /* license with the font vendor may be separately required to use the */ + /* font in this way. */ + /* */ +#define FT_FSTYPE_INSTALLABLE_EMBEDDING 0x0000 +#define FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING 0x0002 +#define FT_FSTYPE_PREVIEW_AND_PRINT_EMBEDDING 0x0004 +#define FT_FSTYPE_EDITABLE_EMBEDDING 0x0008 +#define FT_FSTYPE_NO_SUBSETTING 0x0100 +#define FT_FSTYPE_BITMAP_EMBEDDING_ONLY 0x0200 + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_FSType_Flags */ + /* */ + /* <Description> */ + /* Return the fsType flags for a font. */ + /* */ + /* <Input> */ + /* face :: A handle to the source face object. */ + /* */ + /* <Return> */ + /* The fsType flags, @FT_FSTYPE_XXX. */ + /* */ + /* <Note> */ + /* Use this function rather than directly reading the `fs_type' field */ + /* in the @PS_FontInfoRec structure which is only guaranteed to */ + /* return the correct results for Type~1 fonts. */ + /* */ + FT_EXPORT( FT_UShort ) + FT_Get_FSType_Flags( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* glyph_variants */ + /* */ + /* <Title> */ + /* Glyph Variants */ + /* */ + /* <Abstract> */ + /* The FreeType~2 interface to Unicode Ideographic Variation */ + /* Sequences (IVS), using the SFNT cmap format~14. */ + /* */ + /* <Description> */ + /* Many CJK characters have variant forms. They are a sort of grey */ + /* area somewhere between being totally irrelevant and semantically */ + /* distinct; for this reason, the Unicode consortium decided to */ + /* introduce Ideographic Variation Sequences (IVS), consisting of a */ + /* Unicode base character and one of 240 variant selectors */ + /* (U+E0100-U+E01EF), instead of further extending the already huge */ + /* code range for CJK characters. */ + /* */ + /* An IVS is registered and unique; for further details please refer */ + /* to Unicode Technical Report #37, the Ideographic Variation */ + /* Database. To date (October 2007), the character with the most */ + /* variants is U+908A, having 8~such IVS. */ + /* */ + /* Adobe and MS decided to support IVS with a new cmap subtable */ + /* (format~14). It is an odd subtable because it is not a mapping of */ + /* input code points to glyphs, but contains lists of all variants */ + /* supported by the font. */ + /* */ + /* A variant may be either `default' or `non-default'. A default */ + /* variant is the one you will get for that code point if you look it */ + /* up in the standard Unicode cmap. A non-default variant is a */ + /* different glyph. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_GetCharVariantIndex */ + /* */ + /* <Description> */ + /* Return the glyph index of a given character code as modified by */ + /* the variation selector. */ + /* */ + /* <Input> */ + /* face :: */ + /* A handle to the source face object. */ + /* */ + /* charcode :: */ + /* The character code point in Unicode. */ + /* */ + /* variantSelector :: */ + /* The Unicode code point of the variation selector. */ + /* */ + /* <Return> */ + /* The glyph index. 0~means either `undefined character code', or */ + /* `undefined selector code', or `no variation selector cmap */ + /* subtable', or `current CharMap is not Unicode'. */ + /* */ + /* <Note> */ + /* If you use FreeType to manipulate the contents of font files */ + /* directly, be aware that the glyph index returned by this function */ + /* doesn't always correspond to the internal indices used within */ + /* the file. This is done to ensure that value~0 always corresponds */ + /* to the `missing glyph'. */ + /* */ + /* This function is only meaningful if */ + /* a) the font has a variation selector cmap sub table, */ + /* and */ + /* b) the current charmap has a Unicode encoding. */ + /* */ + /* <Since> */ + /* 2.3.6 */ + /* */ + FT_EXPORT( FT_UInt ) + FT_Face_GetCharVariantIndex( FT_Face face, + FT_ULong charcode, + FT_ULong variantSelector ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_GetCharVariantIsDefault */ + /* */ + /* <Description> */ + /* Check whether this variant of this Unicode character is the one to */ + /* be found in the `cmap'. */ + /* */ + /* <Input> */ + /* face :: */ + /* A handle to the source face object. */ + /* */ + /* charcode :: */ + /* The character codepoint in Unicode. */ + /* */ + /* variantSelector :: */ + /* The Unicode codepoint of the variation selector. */ + /* */ + /* <Return> */ + /* 1~if found in the standard (Unicode) cmap, 0~if found in the */ + /* variation selector cmap, or -1 if it is not a variant. */ + /* */ + /* <Note> */ + /* This function is only meaningful if the font has a variation */ + /* selector cmap subtable. */ + /* */ + /* <Since> */ + /* 2.3.6 */ + /* */ + FT_EXPORT( FT_Int ) + FT_Face_GetCharVariantIsDefault( FT_Face face, + FT_ULong charcode, + FT_ULong variantSelector ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_GetVariantSelectors */ + /* */ + /* <Description> */ + /* Return a zero-terminated list of Unicode variant selectors found */ + /* in the font. */ + /* */ + /* <Input> */ + /* face :: */ + /* A handle to the source face object. */ + /* */ + /* <Return> */ + /* A pointer to an array of selector code points, or NULL if there is */ + /* no valid variant selector cmap subtable. */ + /* */ + /* <Note> */ + /* The last item in the array is~0; the array is owned by the */ + /* @FT_Face object but can be overwritten or released on the next */ + /* call to a FreeType function. */ + /* */ + /* <Since> */ + /* 2.3.6 */ + /* */ + FT_EXPORT( FT_UInt32* ) + FT_Face_GetVariantSelectors( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_GetVariantsOfChar */ + /* */ + /* <Description> */ + /* Return a zero-terminated list of Unicode variant selectors found */ + /* for the specified character code. */ + /* */ + /* <Input> */ + /* face :: */ + /* A handle to the source face object. */ + /* */ + /* charcode :: */ + /* The character codepoint in Unicode. */ + /* */ + /* <Return> */ + /* A pointer to an array of variant selector code points which are */ + /* active for the given character, or NULL if the corresponding list */ + /* is empty. */ + /* */ + /* <Note> */ + /* The last item in the array is~0; the array is owned by the */ + /* @FT_Face object but can be overwritten or released on the next */ + /* call to a FreeType function. */ + /* */ + /* <Since> */ + /* 2.3.6 */ + /* */ + FT_EXPORT( FT_UInt32* ) + FT_Face_GetVariantsOfChar( FT_Face face, + FT_ULong charcode ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_GetCharsOfVariant */ + /* */ + /* <Description> */ + /* Return a zero-terminated list of Unicode character codes found for */ + /* the specified variant selector. */ + /* */ + /* <Input> */ + /* face :: */ + /* A handle to the source face object. */ + /* */ + /* variantSelector :: */ + /* The variant selector code point in Unicode. */ + /* */ + /* <Return> */ + /* A list of all the code points which are specified by this selector */ + /* (both default and non-default codes are returned) or NULL if there */ + /* is no valid cmap or the variant selector is invalid. */ + /* */ + /* <Note> */ + /* The last item in the array is~0; the array is owned by the */ + /* @FT_Face object but can be overwritten or released on the next */ + /* call to a FreeType function. */ + /* */ + /* <Since> */ + /* 2.3.6 */ + /* */ + FT_EXPORT( FT_UInt32* ) + FT_Face_GetCharsOfVariant( FT_Face face, + FT_ULong variantSelector ); + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* computations */ + /* */ + /* <Title> */ + /* Computations */ + /* */ + /* <Abstract> */ + /* Crunching fixed numbers and vectors. */ + /* */ + /* <Description> */ + /* This section contains various functions used to perform */ + /* computations on 16.16 fixed-float numbers or 2d vectors. */ + /* */ + /* <Order> */ + /* FT_MulDiv */ + /* FT_MulFix */ + /* FT_DivFix */ + /* FT_RoundFix */ + /* FT_CeilFix */ + /* FT_FloorFix */ + /* FT_Vector_Transform */ + /* FT_Matrix_Multiply */ + /* FT_Matrix_Invert */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_MulDiv */ + /* */ + /* <Description> */ + /* A very simple function used to perform the computation `(a*b)/c' */ + /* with maximal accuracy (it uses a 64-bit intermediate integer */ + /* whenever necessary). */ + /* */ + /* This function isn't necessarily as fast as some processor specific */ + /* operations, but is at least completely portable. */ + /* */ + /* <Input> */ + /* a :: The first multiplier. */ + /* b :: The second multiplier. */ + /* c :: The divisor. */ + /* */ + /* <Return> */ + /* The result of `(a*b)/c'. This function never traps when trying to */ + /* divide by zero; it simply returns `MaxInt' or `MinInt' depending */ + /* on the signs of `a' and `b'. */ + /* */ + FT_EXPORT( FT_Long ) + FT_MulDiv( FT_Long a, + FT_Long b, + FT_Long c ); + + + /* */ + + /* The following #if 0 ... #endif is for the documentation formatter, */ + /* hiding the internal `FT_MULFIX_INLINED' macro. */ + +#if 0 + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_MulFix */ + /* */ + /* <Description> */ + /* A very simple function used to perform the computation */ + /* `(a*b)/0x10000' with maximal accuracy. Most of the time this is */ + /* used to multiply a given value by a 16.16 fixed float factor. */ + /* */ + /* <Input> */ + /* a :: The first multiplier. */ + /* b :: The second multiplier. Use a 16.16 factor here whenever */ + /* possible (see note below). */ + /* */ + /* <Return> */ + /* The result of `(a*b)/0x10000'. */ + /* */ + /* <Note> */ + /* This function has been optimized for the case where the absolute */ + /* value of `a' is less than 2048, and `b' is a 16.16 scaling factor. */ + /* As this happens mainly when scaling from notional units to */ + /* fractional pixels in FreeType, it resulted in noticeable speed */ + /* improvements between versions 2.x and 1.x. */ + /* */ + /* As a conclusion, always try to place a 16.16 factor as the */ + /* _second_ argument of this function; this can make a great */ + /* difference. */ + /* */ + FT_EXPORT( FT_Long ) + FT_MulFix( FT_Long a, + FT_Long b ); + + /* */ +#endif + +#ifdef FT_MULFIX_INLINED +#define FT_MulFix( a, b ) FT_MULFIX_INLINED( a, b ) +#else + FT_EXPORT( FT_Long ) + FT_MulFix( FT_Long a, + FT_Long b ); +#endif + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_DivFix */ + /* */ + /* <Description> */ + /* A very simple function used to perform the computation */ + /* `(a*0x10000)/b' with maximal accuracy. Most of the time, this is */ + /* used to divide a given value by a 16.16 fixed float factor. */ + /* */ + /* <Input> */ + /* a :: The first multiplier. */ + /* b :: The second multiplier. Use a 16.16 factor here whenever */ + /* possible (see note below). */ + /* */ + /* <Return> */ + /* The result of `(a*0x10000)/b'. */ + /* */ + /* <Note> */ + /* The optimization for FT_DivFix() is simple: If (a~<<~16) fits in */ + /* 32~bits, then the division is computed directly. Otherwise, we */ + /* use a specialized version of @FT_MulDiv. */ + /* */ + FT_EXPORT( FT_Long ) + FT_DivFix( FT_Long a, + FT_Long b ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_RoundFix */ + /* */ + /* <Description> */ + /* A very simple function used to round a 16.16 fixed number. */ + /* */ + /* <Input> */ + /* a :: The number to be rounded. */ + /* */ + /* <Return> */ + /* The result of `(a + 0x8000) & -0x10000'. */ + /* */ + FT_EXPORT( FT_Fixed ) + FT_RoundFix( FT_Fixed a ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_CeilFix */ + /* */ + /* <Description> */ + /* A very simple function used to compute the ceiling function of a */ + /* 16.16 fixed number. */ + /* */ + /* <Input> */ + /* a :: The number for which the ceiling function is to be computed. */ + /* */ + /* <Return> */ + /* The result of `(a + 0x10000 - 1) & -0x10000'. */ + /* */ + FT_EXPORT( FT_Fixed ) + FT_CeilFix( FT_Fixed a ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_FloorFix */ + /* */ + /* <Description> */ + /* A very simple function used to compute the floor function of a */ + /* 16.16 fixed number. */ + /* */ + /* <Input> */ + /* a :: The number for which the floor function is to be computed. */ + /* */ + /* <Return> */ + /* The result of `a & -0x10000'. */ + /* */ + FT_EXPORT( FT_Fixed ) + FT_FloorFix( FT_Fixed a ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Vector_Transform */ + /* */ + /* <Description> */ + /* Transform a single vector through a 2x2 matrix. */ + /* */ + /* <InOut> */ + /* vector :: The target vector to transform. */ + /* */ + /* <Input> */ + /* matrix :: A pointer to the source 2x2 matrix. */ + /* */ + /* <Note> */ + /* The result is undefined if either `vector' or `matrix' is invalid. */ + /* */ + FT_EXPORT( void ) + FT_Vector_Transform( FT_Vector* vec, + const FT_Matrix* matrix ); + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* version */ + /* */ + /* <Title> */ + /* FreeType Version */ + /* */ + /* <Abstract> */ + /* Functions and macros related to FreeType versions. */ + /* */ + /* <Description> */ + /* Note that those functions and macros are of limited use because */ + /* even a new release of FreeType with only documentation changes */ + /* increases the version number. */ + /* */ + /*************************************************************************/ + + + /************************************************************************* + * + * @enum: + * FREETYPE_XXX + * + * @description: + * These three macros identify the FreeType source code version. + * Use @FT_Library_Version to access them at runtime. + * + * @values: + * FREETYPE_MAJOR :: The major version number. + * FREETYPE_MINOR :: The minor version number. + * FREETYPE_PATCH :: The patch level. + * + * @note: + * The version number of FreeType if built as a dynamic link library + * with the `libtool' package is _not_ controlled by these three + * macros. + * + */ +#define FREETYPE_MAJOR 2 +#define FREETYPE_MINOR 3 +#define FREETYPE_PATCH 9 + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Library_Version */ + /* */ + /* <Description> */ + /* Return the version of the FreeType library being used. This is */ + /* useful when dynamically linking to the library, since one cannot */ + /* use the macros @FREETYPE_MAJOR, @FREETYPE_MINOR, and */ + /* @FREETYPE_PATCH. */ + /* */ + /* <Input> */ + /* library :: A source library handle. */ + /* */ + /* <Output> */ + /* amajor :: The major version number. */ + /* */ + /* aminor :: The minor version number. */ + /* */ + /* apatch :: The patch version number. */ + /* */ + /* <Note> */ + /* The reason why this function takes a `library' argument is because */ + /* certain programs implement library initialization in a custom way */ + /* that doesn't use @FT_Init_FreeType. */ + /* */ + /* In such cases, the library version might not be available before */ + /* the library object has been created. */ + /* */ + FT_EXPORT( void ) + FT_Library_Version( FT_Library library, + FT_Int *amajor, + FT_Int *aminor, + FT_Int *apatch ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_CheckTrueTypePatents */ + /* */ + /* <Description> */ + /* Parse all bytecode instructions of a TrueType font file to check */ + /* whether any of the patented opcodes are used. This is only useful */ + /* if you want to be able to use the unpatented hinter with */ + /* fonts that do *not* use these opcodes. */ + /* */ + /* Note that this function parses *all* glyph instructions in the */ + /* font file, which may be slow. */ + /* */ + /* <Input> */ + /* face :: A face handle. */ + /* */ + /* <Return> */ + /* 1~if this is a TrueType font that uses one of the patented */ + /* opcodes, 0~otherwise. */ + /* */ + /* <Since> */ + /* 2.3.5 */ + /* */ + FT_EXPORT( FT_Bool ) + FT_Face_CheckTrueTypePatents( FT_Face face ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Face_SetUnpatentedHinting */ + /* */ + /* <Description> */ + /* Enable or disable the unpatented hinter for a given face. */ + /* Only enable it if you have determined that the face doesn't */ + /* use any patented opcodes (see @FT_Face_CheckTrueTypePatents). */ + /* */ + /* <Input> */ + /* face :: A face handle. */ + /* */ + /* value :: New boolean setting. */ + /* */ + /* <Return> */ + /* The old setting value. This will always be false if this is not */ + /* an SFNT font, or if the unpatented hinter is not compiled in this */ + /* instance of the library. */ + /* */ + /* <Since> */ + /* 2.3.5 */ + /* */ + FT_EXPORT( FT_Bool ) + FT_Face_SetUnpatentedHinting( FT_Face face, + FT_Bool value ); + + /* */ + + +FT_END_HEADER + +#endif /* __FREETYPE_H__ */ + + +/* END */ diff --git a/guilib/freetype2/include/freetype/ftcache.h b/guilib/freetype2/include/freetype/ftcache.h new file mode 100644 index 0000000000..230d6f2a8c --- /dev/null +++ b/guilib/freetype2/include/freetype/ftcache.h @@ -0,0 +1,1125 @@ +/***************************************************************************/
+/* */
+/* ftcache.h */
+/* */
+/* FreeType Cache subsystem (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __FTCACHE_H__
+#define __FTCACHE_H__
+
+
+#include <ft2build.h>
+#include FT_GLYPH_H
+
+
+FT_BEGIN_HEADER
+
+
+ /*************************************************************************
+ *
+ * <Section>
+ * cache_subsystem
+ *
+ * <Title>
+ * Cache Sub-System
+ *
+ * <Abstract>
+ * How to cache face, size, and glyph data with FreeType~2.
+ *
+ * <Description>
+ * This section describes the FreeType~2 cache sub-system, which is used
+ * to limit the number of concurrently opened @FT_Face and @FT_Size
+ * objects, as well as caching information like character maps and glyph
+ * images while limiting their maximum memory usage.
+ *
+ * Note that all types and functions begin with the `FTC_' prefix.
+ *
+ * The cache is highly portable and thus doesn't know anything about the
+ * fonts installed on your system, or how to access them. This implies
+ * the following scheme:
+ *
+ * First, available or installed font faces are uniquely identified by
+ * @FTC_FaceID values, provided to the cache by the client. Note that
+ * the cache only stores and compares these values, and doesn't try to
+ * interpret them in any way.
+ *
+ * Second, the cache calls, only when needed, a client-provided function
+ * to convert a @FTC_FaceID into a new @FT_Face object. The latter is
+ * then completely managed by the cache, including its termination
+ * through @FT_Done_Face.
+ *
+ * Clients are free to map face IDs to anything else. The most simple
+ * usage is to associate them to a (pathname,face_index) pair that is
+ * used to call @FT_New_Face. However, more complex schemes are also
+ * possible.
+ *
+ * Note that for the cache to work correctly, the face ID values must be
+ * *persistent*, which means that the contents they point to should not
+ * change at runtime, or that their value should not become invalid.
+ *
+ * If this is unavoidable (e.g., when a font is uninstalled at runtime),
+ * you should call @FTC_Manager_RemoveFaceID as soon as possible, to let
+ * the cache get rid of any references to the old @FTC_FaceID it may
+ * keep internally. Failure to do so will lead to incorrect behaviour
+ * or even crashes.
+ *
+ * To use the cache, start with calling @FTC_Manager_New to create a new
+ * @FTC_Manager object, which models a single cache instance. You can
+ * then look up @FT_Face and @FT_Size objects with
+ * @FTC_Manager_LookupFace and @FTC_Manager_LookupSize, respectively.
+ *
+ * If you want to use the charmap caching, call @FTC_CMapCache_New, then
+ * later use @FTC_CMapCache_Lookup to perform the equivalent of
+ * @FT_Get_Char_Index, only much faster.
+ *
+ * If you want to use the @FT_Glyph caching, call @FTC_ImageCache, then
+ * later use @FTC_ImageCache_Lookup to retrieve the corresponding
+ * @FT_Glyph objects from the cache.
+ *
+ * If you need lots of small bitmaps, it is much more memory efficient
+ * to call @FTC_SBitCache_New followed by @FTC_SBitCache_Lookup. This
+ * returns @FTC_SBitRec structures, which are used to store small
+ * bitmaps directly. (A small bitmap is one whose metrics and
+ * dimensions all fit into 8-bit integers).
+ *
+ * We hope to also provide a kerning cache in the near future.
+ *
+ *
+ * <Order>
+ * FTC_Manager
+ * FTC_FaceID
+ * FTC_Face_Requester
+ *
+ * FTC_Manager_New
+ * FTC_Manager_Reset
+ * FTC_Manager_Done
+ * FTC_Manager_LookupFace
+ * FTC_Manager_LookupSize
+ * FTC_Manager_RemoveFaceID
+ *
+ * FTC_Node
+ * FTC_Node_Unref
+ *
+ * FTC_ImageCache
+ * FTC_ImageCache_New
+ * FTC_ImageCache_Lookup
+ *
+ * FTC_SBit
+ * FTC_SBitCache
+ * FTC_SBitCache_New
+ * FTC_SBitCache_Lookup
+ *
+ * FTC_CMapCache
+ * FTC_CMapCache_New
+ * FTC_CMapCache_Lookup
+ *
+ *************************************************************************/
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** BASIC TYPE DEFINITIONS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************
+ *
+ * @type: FTC_FaceID
+ *
+ * @description:
+ * An opaque pointer type that is used to identity face objects. The
+ * contents of such objects is application-dependent.
+ *
+ * These pointers are typically used to point to a user-defined
+ * structure containing a font file path, and face index.
+ *
+ * @note:
+ * Never use NULL as a valid @FTC_FaceID.
+ *
+ * Face IDs are passed by the client to the cache manager, which calls,
+ * when needed, the @FTC_Face_Requester to translate them into new
+ * @FT_Face objects.
+ *
+ * If the content of a given face ID changes at runtime, or if the value
+ * becomes invalid (e.g., when uninstalling a font), you should
+ * immediately call @FTC_Manager_RemoveFaceID before any other cache
+ * function.
+ *
+ * Failure to do so will result in incorrect behaviour or even
+ * memory leaks and crashes.
+ */
+ typedef FT_Pointer FTC_FaceID;
+
+
+ /************************************************************************
+ *
+ * @functype:
+ * FTC_Face_Requester
+ *
+ * @description:
+ * A callback function provided by client applications. It is used by
+ * the cache manager to translate a given @FTC_FaceID into a new valid
+ * @FT_Face object, on demand.
+ *
+ * <Input>
+ * face_id ::
+ * The face ID to resolve.
+ *
+ * library ::
+ * A handle to a FreeType library object.
+ *
+ * req_data ::
+ * Application-provided request data (see note below).
+ *
+ * <Output>
+ * aface ::
+ * A new @FT_Face handle.
+ *
+ * <Return>
+ * FreeType error code. 0~means success.
+ *
+ * <Note>
+ * The third parameter `req_data' is the same as the one passed by the
+ * client when @FTC_Manager_New is called.
+ *
+ * The face requester should not perform funny things on the returned
+ * face object, like creating a new @FT_Size for it, or setting a
+ * transformation through @FT_Set_Transform!
+ */
+ typedef FT_Error
+ (*FTC_Face_Requester)( FTC_FaceID face_id,
+ FT_Library library,
+ FT_Pointer request_data,
+ FT_Face* aface );
+
+ /* */
+
+#define FT_POINTER_TO_ULONG( p ) ( (FT_ULong)(FT_Pointer)(p) )
+
+#define FTC_FACE_ID_HASH( i ) \
+ ((FT_UInt32)(( FT_POINTER_TO_ULONG( i ) >> 3 ) ^ \
+ ( FT_POINTER_TO_ULONG( i ) << 7 ) ) )
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** CACHE MANAGER OBJECT *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* FTC_Manager */
+ /* */
+ /* <Description> */
+ /* This object corresponds to one instance of the cache-subsystem. */
+ /* It is used to cache one or more @FT_Face objects, along with */
+ /* corresponding @FT_Size objects. */
+ /* */
+ /* The manager intentionally limits the total number of opened */
+ /* @FT_Face and @FT_Size objects to control memory usage. See the */
+ /* `max_faces' and `max_sizes' parameters of @FTC_Manager_New. */
+ /* */
+ /* The manager is also used to cache `nodes' of various types while */
+ /* limiting their total memory usage. */
+ /* */
+ /* All limitations are enforced by keeping lists of managed objects */
+ /* in most-recently-used order, and flushing old nodes to make room */
+ /* for new ones. */
+ /* */
+ typedef struct FTC_ManagerRec_* FTC_Manager;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* FTC_Node */
+ /* */
+ /* <Description> */
+ /* An opaque handle to a cache node object. Each cache node is */
+ /* reference-counted. A node with a count of~0 might be flushed */
+ /* out of a full cache whenever a lookup request is performed. */
+ /* */
+ /* If you lookup nodes, you have the ability to `acquire' them, i.e., */
+ /* to increment their reference count. This will prevent the node */
+ /* from being flushed out of the cache until you explicitly `release' */
+ /* it (see @FTC_Node_Unref). */
+ /* */
+ /* See also @FTC_SBitCache_Lookup and @FTC_ImageCache_Lookup. */
+ /* */
+ typedef struct FTC_NodeRec_* FTC_Node;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* FTC_Manager_New */
+ /* */
+ /* <Description> */
+ /* Create a new cache manager. */
+ /* */
+ /* <Input> */
+ /* library :: The parent FreeType library handle to use. */
+ /* */
+ /* max_faces :: Maximum number of opened @FT_Face objects managed by */
+ /* this cache instance. Use~0 for defaults. */
+ /* */
+ /* max_sizes :: Maximum number of opened @FT_Size objects managed by */
+ /* this cache instance. Use~0 for defaults. */
+ /* */
+ /* max_bytes :: Maximum number of bytes to use for cached data nodes. */
+ /* Use~0 for defaults. Note that this value does not */
+ /* account for managed @FT_Face and @FT_Size objects. */
+ /* */
+ /* requester :: An application-provided callback used to translate */
+ /* face IDs into real @FT_Face objects. */
+ /* */
+ /* req_data :: A generic pointer that is passed to the requester */
+ /* each time it is called (see @FTC_Face_Requester). */
+ /* */
+ /* <Output> */
+ /* amanager :: A handle to a new manager object. 0~in case of */
+ /* failure. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0~means success. */
+ /* */
+ FT_EXPORT( FT_Error )
+ FTC_Manager_New( FT_Library library,
+ FT_UInt max_faces,
+ FT_UInt max_sizes,
+ FT_ULong max_bytes,
+ FTC_Face_Requester requester,
+ FT_Pointer req_data,
+ FTC_Manager *amanager );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* FTC_Manager_Reset */
+ /* */
+ /* <Description> */
+ /* Empty a given cache manager. This simply gets rid of all the */
+ /* currently cached @FT_Face and @FT_Size objects within the manager. */
+ /* */
+ /* <InOut> */
+ /* manager :: A handle to the manager. */
+ /* */
+ FT_EXPORT( void )
+ FTC_Manager_Reset( FTC_Manager manager );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* FTC_Manager_Done */
+ /* */
+ /* <Description> */
+ /* Destroy a given manager after emptying it. */
+ /* */
+ /* <Input> */
+ /* manager :: A handle to the target cache manager object. */
+ /* */
+ FT_EXPORT( void )
+ FTC_Manager_Done( FTC_Manager manager );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* FTC_Manager_LookupFace */
+ /* */
+ /* <Description> */
+ /* Retrieve the @FT_Face object that corresponds to a given face ID */
+ /* through a cache manager. */
+ /* */
+ /* <Input> */
+ /* manager :: A handle to the cache manager. */
+ /* */
+ /* face_id :: The ID of the face object. */
+ /* */
+ /* <Output> */
+ /* aface :: A handle to the face object. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0~means success. */
+ /* */
+ /* <Note> */
+ /* The returned @FT_Face object is always owned by the manager. You */
+ /* should never try to discard it yourself. */
+ /* */
+ /* The @FT_Face object doesn't necessarily have a current size object */
+ /* (i.e., face->size can be 0). If you need a specific `font size', */
+ /* use @FTC_Manager_LookupSize instead. */
+ /* */
+ /* Never change the face's transformation matrix (i.e., never call */
+ /* the @FT_Set_Transform function) on a returned face! If you need */
+ /* to transform glyphs, do it yourself after glyph loading. */
+ /* */
+ /* When you perform a lookup, out-of-memory errors are detected */
+ /* _within_ the lookup and force incremental flushes of the cache */
+ /* until enough memory is released for the lookup to succeed. */
+ /* */
+ /* If a lookup fails with `FT_Err_Out_Of_Memory' the cache has */
+ /* already been completely flushed, and still no memory was available */
+ /* for the operation. */
+ /* */
+ FT_EXPORT( FT_Error )
+ FTC_Manager_LookupFace( FTC_Manager manager,
+ FTC_FaceID face_id,
+ FT_Face *aface );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* FTC_ScalerRec */
+ /* */
+ /* <Description> */
+ /* A structure used to describe a given character size in either */
+ /* pixels or points to the cache manager. See */
+ /* @FTC_Manager_LookupSize. */
+ /* */
+ /* <Fields> */
+ /* face_id :: The source face ID. */
+ /* */
+ /* width :: The character width. */
+ /* */
+ /* height :: The character height. */
+ /* */
+ /* pixel :: A Boolean. If 1, the `width' and `height' fields are */
+ /* interpreted as integer pixel character sizes. */
+ /* Otherwise, they are expressed as 1/64th of points. */
+ /* */
+ /* x_res :: Only used when `pixel' is value~0 to indicate the */
+ /* horizontal resolution in dpi. */
+ /* */
+ /* y_res :: Only used when `pixel' is value~0 to indicate the */
+ /* vertical resolution in dpi. */
+ /* */
+ /* <Note> */
+ /* This type is mainly used to retrieve @FT_Size objects through the */
+ /* cache manager. */
+ /* */
+ typedef struct FTC_ScalerRec_
+ {
+ FTC_FaceID face_id;
+ FT_UInt width;
+ FT_UInt height;
+ FT_Int pixel;
+ FT_UInt x_res;
+ FT_UInt y_res;
+
+ } FTC_ScalerRec;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* FTC_Scaler */
+ /* */
+ /* <Description> */
+ /* A handle to an @FTC_ScalerRec structure. */
+ /* */
+ typedef struct FTC_ScalerRec_* FTC_Scaler;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* FTC_Manager_LookupSize */
+ /* */
+ /* <Description> */
+ /* Retrieve the @FT_Size object that corresponds to a given */
+ /* @FTC_ScalerRec pointer through a cache manager. */
+ /* */
+ /* <Input> */
+ /* manager :: A handle to the cache manager. */
+ /* */
+ /* scaler :: A scaler handle. */
+ /* */
+ /* <Output> */
+ /* asize :: A handle to the size object. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0~means success. */
+ /* */
+ /* <Note> */
+ /* The returned @FT_Size object is always owned by the manager. You */
+ /* should never try to discard it by yourself. */
+ /* */
+ /* You can access the parent @FT_Face object simply as `size->face' */
+ /* if you need it. Note that this object is also owned by the */
+ /* manager. */
+ /* */
+ /* <Note> */
+ /* When you perform a lookup, out-of-memory errors are detected */
+ /* _within_ the lookup and force incremental flushes of the cache */
+ /* until enough memory is released for the lookup to succeed. */
+ /* */
+ /* If a lookup fails with `FT_Err_Out_Of_Memory' the cache has */
+ /* already been completely flushed, and still no memory is available */
+ /* for the operation. */
+ /* */
+ FT_EXPORT( FT_Error )
+ FTC_Manager_LookupSize( FTC_Manager manager,
+ FTC_Scaler scaler,
+ FT_Size *asize );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* FTC_Node_Unref */
+ /* */
+ /* <Description> */
+ /* Decrement a cache node's internal reference count. When the count */
+ /* reaches 0, it is not destroyed but becomes eligible for subsequent */
+ /* cache flushes. */
+ /* */
+ /* <Input> */
+ /* node :: The cache node handle. */
+ /* */
+ /* manager :: The cache manager handle. */
+ /* */
+ FT_EXPORT( void )
+ FTC_Node_Unref( FTC_Node node,
+ FTC_Manager manager );
+
+
+ /*************************************************************************
+ *
+ * @function:
+ * FTC_Manager_RemoveFaceID
+ *
+ * @description:
+ * A special function used to indicate to the cache manager that
+ * a given @FTC_FaceID is no longer valid, either because its
+ * content changed, or because it was deallocated or uninstalled.
+ *
+ * @input:
+ * manager ::
+ * The cache manager handle.
+ *
+ * face_id ::
+ * The @FTC_FaceID to be removed.
+ *
+ * @note:
+ * This function flushes all nodes from the cache corresponding to this
+ * `face_id', with the exception of nodes with a non-null reference
+ * count.
+ *
+ * Such nodes are however modified internally so as to never appear
+ * in later lookups with the same `face_id' value, and to be immediately
+ * destroyed when released by all their users.
+ *
+ */
+ FT_EXPORT( void )
+ FTC_Manager_RemoveFaceID( FTC_Manager manager,
+ FTC_FaceID face_id );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Section> */
+ /* cache_subsystem */
+ /* */
+ /*************************************************************************/
+
+ /*************************************************************************
+ *
+ * @type:
+ * FTC_CMapCache
+ *
+ * @description:
+ * An opaque handle used to model a charmap cache. This cache is to
+ * hold character codes -> glyph indices mappings.
+ *
+ */
+ typedef struct FTC_CMapCacheRec_* FTC_CMapCache;
+
+
+ /*************************************************************************
+ *
+ * @function:
+ * FTC_CMapCache_New
+ *
+ * @description:
+ * Create a new charmap cache.
+ *
+ * @input:
+ * manager ::
+ * A handle to the cache manager.
+ *
+ * @output:
+ * acache ::
+ * A new cache handle. NULL in case of error.
+ *
+ * @return:
+ * FreeType error code. 0~means success.
+ *
+ * @note:
+ * Like all other caches, this one will be destroyed with the cache
+ * manager.
+ *
+ */
+ FT_EXPORT( FT_Error )
+ FTC_CMapCache_New( FTC_Manager manager,
+ FTC_CMapCache *acache );
+
+
+ /************************************************************************
+ *
+ * @function:
+ * FTC_CMapCache_Lookup
+ *
+ * @description:
+ * Translate a character code into a glyph index, using the charmap
+ * cache.
+ *
+ * @input:
+ * cache ::
+ * A charmap cache handle.
+ *
+ * face_id ::
+ * The source face ID.
+ *
+ * cmap_index ::
+ * The index of the charmap in the source face. Any negative value
+ * means to use the cache @FT_Face's default charmap.
+ *
+ * char_code ::
+ * The character code (in the corresponding charmap).
+ *
+ * @return:
+ * Glyph index. 0~means `no glyph'.
+ *
+ */
+ FT_EXPORT( FT_UInt )
+ FTC_CMapCache_Lookup( FTC_CMapCache cache,
+ FTC_FaceID face_id,
+ FT_Int cmap_index,
+ FT_UInt32 char_code );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Section> */
+ /* cache_subsystem */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** IMAGE CACHE OBJECT *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************
+ *
+ * @struct:
+ * FTC_ImageTypeRec
+ *
+ * @description:
+ * A structure used to model the type of images in a glyph cache.
+ *
+ * @fields:
+ * face_id ::
+ * The face ID.
+ *
+ * width ::
+ * The width in pixels.
+ *
+ * height ::
+ * The height in pixels.
+ *
+ * flags ::
+ * The load flags, as in @FT_Load_Glyph.
+ *
+ */
+ typedef struct FTC_ImageTypeRec_
+ {
+ FTC_FaceID face_id;
+ FT_Int width;
+ FT_Int height;
+ FT_Int32 flags;
+
+ } FTC_ImageTypeRec;
+
+
+ /*************************************************************************
+ *
+ * @type:
+ * FTC_ImageType
+ *
+ * @description:
+ * A handle to an @FTC_ImageTypeRec structure.
+ *
+ */
+ typedef struct FTC_ImageTypeRec_* FTC_ImageType;
+
+
+ /* */
+
+
+#define FTC_IMAGE_TYPE_COMPARE( d1, d2 ) \
+ ( (d1)->face_id == (d2)->face_id && \
+ (d1)->width == (d2)->width && \
+ (d1)->flags == (d2)->flags )
+
+#define FTC_IMAGE_TYPE_HASH( d ) \
+ (FT_UFast)( FTC_FACE_ID_HASH( (d)->face_id ) ^ \
+ ( (d)->width << 8 ) ^ (d)->height ^ \
+ ( (d)->flags << 4 ) )
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* FTC_ImageCache */
+ /* */
+ /* <Description> */
+ /* A handle to an glyph image cache object. They are designed to */
+ /* hold many distinct glyph images while not exceeding a certain */
+ /* memory threshold. */
+ /* */
+ typedef struct FTC_ImageCacheRec_* FTC_ImageCache;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* FTC_ImageCache_New */
+ /* */
+ /* <Description> */
+ /* Create a new glyph image cache. */
+ /* */
+ /* <Input> */
+ /* manager :: The parent manager for the image cache. */
+ /* */
+ /* <Output> */
+ /* acache :: A handle to the new glyph image cache object. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0~means success. */
+ /* */
+ FT_EXPORT( FT_Error )
+ FTC_ImageCache_New( FTC_Manager manager,
+ FTC_ImageCache *acache );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* FTC_ImageCache_Lookup */
+ /* */
+ /* <Description> */
+ /* Retrieve a given glyph image from a glyph image cache. */
+ /* */
+ /* <Input> */
+ /* cache :: A handle to the source glyph image cache. */
+ /* */
+ /* type :: A pointer to a glyph image type descriptor. */
+ /* */
+ /* gindex :: The glyph index to retrieve. */
+ /* */
+ /* <Output> */
+ /* aglyph :: The corresponding @FT_Glyph object. 0~in case of */
+ /* failure. */
+ /* */
+ /* anode :: Used to return the address of of the corresponding cache */
+ /* node after incrementing its reference count (see note */
+ /* below). */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0~means success. */
+ /* */
+ /* <Note> */
+ /* The returned glyph is owned and managed by the glyph image cache. */
+ /* Never try to transform or discard it manually! You can however */
+ /* create a copy with @FT_Glyph_Copy and modify the new one. */
+ /* */
+ /* If `anode' is _not_ NULL, it receives the address of the cache */
+ /* node containing the glyph image, after increasing its reference */
+ /* count. This ensures that the node (as well as the @FT_Glyph) will */
+ /* always be kept in the cache until you call @FTC_Node_Unref to */
+ /* `release' it. */
+ /* */
+ /* If `anode' is NULL, the cache node is left unchanged, which means */
+ /* that the @FT_Glyph could be flushed out of the cache on the next */
+ /* call to one of the caching sub-system APIs. Don't assume that it */
+ /* is persistent! */
+ /* */
+ FT_EXPORT( FT_Error )
+ FTC_ImageCache_Lookup( FTC_ImageCache cache,
+ FTC_ImageType type,
+ FT_UInt gindex,
+ FT_Glyph *aglyph,
+ FTC_Node *anode );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* FTC_ImageCache_LookupScaler */
+ /* */
+ /* <Description> */
+ /* A variant of @FTC_ImageCache_Lookup that uses an @FTC_ScalerRec */
+ /* to specify the face ID and its size. */
+ /* */
+ /* <Input> */
+ /* cache :: A handle to the source glyph image cache. */
+ /* */
+ /* scaler :: A pointer to a scaler descriptor. */
+ /* */
+ /* load_flags :: The corresponding load flags. */
+ /* */
+ /* gindex :: The glyph index to retrieve. */
+ /* */
+ /* <Output> */
+ /* aglyph :: The corresponding @FT_Glyph object. 0~in case of */
+ /* failure. */
+ /* */
+ /* anode :: Used to return the address of of the corresponding */
+ /* cache node after incrementing its reference count */
+ /* (see note below). */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0~means success. */
+ /* */
+ /* <Note> */
+ /* The returned glyph is owned and managed by the glyph image cache. */
+ /* Never try to transform or discard it manually! You can however */
+ /* create a copy with @FT_Glyph_Copy and modify the new one. */
+ /* */
+ /* If `anode' is _not_ NULL, it receives the address of the cache */
+ /* node containing the glyph image, after increasing its reference */
+ /* count. This ensures that the node (as well as the @FT_Glyph) will */
+ /* always be kept in the cache until you call @FTC_Node_Unref to */
+ /* `release' it. */
+ /* */
+ /* If `anode' is NULL, the cache node is left unchanged, which means */
+ /* that the @FT_Glyph could be flushed out of the cache on the next */
+ /* call to one of the caching sub-system APIs. Don't assume that it */
+ /* is persistent! */
+ /* */
+ /* Calls to @FT_Set_Char_Size and friends have no effect on cached */
+ /* glyphs; you should always use the FreeType cache API instead. */
+ /* */
+ FT_EXPORT( FT_Error )
+ FTC_ImageCache_LookupScaler( FTC_ImageCache cache,
+ FTC_Scaler scaler,
+ FT_ULong load_flags,
+ FT_UInt gindex,
+ FT_Glyph *aglyph,
+ FTC_Node *anode );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* FTC_SBit */
+ /* */
+ /* <Description> */
+ /* A handle to a small bitmap descriptor. See the @FTC_SBitRec */
+ /* structure for details. */
+ /* */
+ typedef struct FTC_SBitRec_* FTC_SBit;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* FTC_SBitRec */
+ /* */
+ /* <Description> */
+ /* A very compact structure used to describe a small glyph bitmap. */
+ /* */
+ /* <Fields> */
+ /* width :: The bitmap width in pixels. */
+ /* */
+ /* height :: The bitmap height in pixels. */
+ /* */
+ /* left :: The horizontal distance from the pen position to the */
+ /* left bitmap border (a.k.a. `left side bearing', or */
+ /* `lsb'). */
+ /* */
+ /* top :: The vertical distance from the pen position (on the */
+ /* baseline) to the upper bitmap border (a.k.a. `top */
+ /* side bearing'). The distance is positive for upwards */
+ /* y~coordinates. */
+ /* */
+ /* format :: The format of the glyph bitmap (monochrome or gray). */
+ /* */
+ /* max_grays :: Maximum gray level value (in the range 1 to~255). */
+ /* */
+ /* pitch :: The number of bytes per bitmap line. May be positive */
+ /* or negative. */
+ /* */
+ /* xadvance :: The horizontal advance width in pixels. */
+ /* */
+ /* yadvance :: The vertical advance height in pixels. */
+ /* */
+ /* buffer :: A pointer to the bitmap pixels. */
+ /* */
+ typedef struct FTC_SBitRec_
+ {
+ FT_Byte width;
+ FT_Byte height;
+ FT_Char left;
+ FT_Char top;
+
+ FT_Byte format;
+ FT_Byte max_grays;
+ FT_Short pitch;
+ FT_Char xadvance;
+ FT_Char yadvance;
+
+ FT_Byte* buffer;
+
+ } FTC_SBitRec;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* FTC_SBitCache */
+ /* */
+ /* <Description> */
+ /* A handle to a small bitmap cache. These are special cache objects */
+ /* used to store small glyph bitmaps (and anti-aliased pixmaps) in a */
+ /* much more efficient way than the traditional glyph image cache */
+ /* implemented by @FTC_ImageCache. */
+ /* */
+ typedef struct FTC_SBitCacheRec_* FTC_SBitCache;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* FTC_SBitCache_New */
+ /* */
+ /* <Description> */
+ /* Create a new cache to store small glyph bitmaps. */
+ /* */
+ /* <Input> */
+ /* manager :: A handle to the source cache manager. */
+ /* */
+ /* <Output> */
+ /* acache :: A handle to the new sbit cache. NULL in case of error. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0~means success. */
+ /* */
+ FT_EXPORT( FT_Error )
+ FTC_SBitCache_New( FTC_Manager manager,
+ FTC_SBitCache *acache );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* FTC_SBitCache_Lookup */
+ /* */
+ /* <Description> */
+ /* Look up a given small glyph bitmap in a given sbit cache and */
+ /* `lock' it to prevent its flushing from the cache until needed. */
+ /* */
+ /* <Input> */
+ /* cache :: A handle to the source sbit cache. */
+ /* */
+ /* type :: A pointer to the glyph image type descriptor. */
+ /* */
+ /* gindex :: The glyph index. */
+ /* */
+ /* <Output> */
+ /* sbit :: A handle to a small bitmap descriptor. */
+ /* */
+ /* anode :: Used to return the address of of the corresponding cache */
+ /* node after incrementing its reference count (see note */
+ /* below). */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0~means success. */
+ /* */
+ /* <Note> */
+ /* The small bitmap descriptor and its bit buffer are owned by the */
+ /* cache and should never be freed by the application. They might */
+ /* as well disappear from memory on the next cache lookup, so don't */
+ /* treat them as persistent data. */
+ /* */
+ /* The descriptor's `buffer' field is set to~0 to indicate a missing */
+ /* glyph bitmap. */
+ /* */
+ /* If `anode' is _not_ NULL, it receives the address of the cache */
+ /* node containing the bitmap, after increasing its reference count. */
+ /* This ensures that the node (as well as the image) will always be */
+ /* kept in the cache until you call @FTC_Node_Unref to `release' it. */
+ /* */
+ /* If `anode' is NULL, the cache node is left unchanged, which means */
+ /* that the bitmap could be flushed out of the cache on the next */
+ /* call to one of the caching sub-system APIs. Don't assume that it */
+ /* is persistent! */
+ /* */
+ FT_EXPORT( FT_Error )
+ FTC_SBitCache_Lookup( FTC_SBitCache cache,
+ FTC_ImageType type,
+ FT_UInt gindex,
+ FTC_SBit *sbit,
+ FTC_Node *anode );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* FTC_SBitCache_LookupScaler */
+ /* */
+ /* <Description> */
+ /* A variant of @FTC_SBitCache_Lookup that uses an @FTC_ScalerRec */
+ /* to specify the face ID and its size. */
+ /* */
+ /* <Input> */
+ /* cache :: A handle to the source sbit cache. */
+ /* */
+ /* scaler :: A pointer to the scaler descriptor. */
+ /* */
+ /* load_flags :: The corresponding load flags. */
+ /* */
+ /* gindex :: The glyph index. */
+ /* */
+ /* <Output> */
+ /* sbit :: A handle to a small bitmap descriptor. */
+ /* */
+ /* anode :: Used to return the address of of the corresponding */
+ /* cache node after incrementing its reference count */
+ /* (see note below). */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0~means success. */
+ /* */
+ /* <Note> */
+ /* The small bitmap descriptor and its bit buffer are owned by the */
+ /* cache and should never be freed by the application. They might */
+ /* as well disappear from memory on the next cache lookup, so don't */
+ /* treat them as persistent data. */
+ /* */
+ /* The descriptor's `buffer' field is set to~0 to indicate a missing */
+ /* glyph bitmap. */
+ /* */
+ /* If `anode' is _not_ NULL, it receives the address of the cache */
+ /* node containing the bitmap, after increasing its reference count. */
+ /* This ensures that the node (as well as the image) will always be */
+ /* kept in the cache until you call @FTC_Node_Unref to `release' it. */
+ /* */
+ /* If `anode' is NULL, the cache node is left unchanged, which means */
+ /* that the bitmap could be flushed out of the cache on the next */
+ /* call to one of the caching sub-system APIs. Don't assume that it */
+ /* is persistent! */
+ /* */
+ FT_EXPORT( FT_Error )
+ FTC_SBitCache_LookupScaler( FTC_SBitCache cache,
+ FTC_Scaler scaler,
+ FT_ULong load_flags,
+ FT_UInt gindex,
+ FTC_SBit *sbit,
+ FTC_Node *anode );
+
+
+ /* */
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+
+ /*@***********************************************************************/
+ /* */
+ /* <Struct> */
+ /* FTC_FontRec */
+ /* */
+ /* <Description> */
+ /* A simple structure used to describe a given `font' to the cache */
+ /* manager. Note that a `font' is the combination of a given face */
+ /* with a given character size. */
+ /* */
+ /* <Fields> */
+ /* face_id :: The ID of the face to use. */
+ /* */
+ /* pix_width :: The character width in integer pixels. */
+ /* */
+ /* pix_height :: The character height in integer pixels. */
+ /* */
+ typedef struct FTC_FontRec_
+ {
+ FTC_FaceID face_id;
+ FT_UShort pix_width;
+ FT_UShort pix_height;
+
+ } FTC_FontRec;
+
+
+ /* */
+
+
+#define FTC_FONT_COMPARE( f1, f2 ) \
+ ( (f1)->face_id == (f2)->face_id && \
+ (f1)->pix_width == (f2)->pix_width && \
+ (f1)->pix_height == (f2)->pix_height )
+
+#define FTC_FONT_HASH( f ) \
+ (FT_UInt32)( FTC_FACE_ID_HASH((f)->face_id) ^ \
+ ((f)->pix_width << 8) ^ \
+ ((f)->pix_height) )
+
+ typedef FTC_FontRec* FTC_Font;
+
+
+ FT_EXPORT( FT_Error )
+ FTC_Manager_Lookup_Face( FTC_Manager manager,
+ FTC_FaceID face_id,
+ FT_Face *aface );
+
+ FT_EXPORT( FT_Error )
+ FTC_Manager_Lookup_Size( FTC_Manager manager,
+ FTC_Font font,
+ FT_Face *aface,
+ FT_Size *asize );
+
+#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
+
+
+ /* */
+
+FT_END_HEADER
+
+#endif /* __FTCACHE_H__ */
+
+
+/* END */
diff --git a/guilib/freetype2/include/freetype/fterrdef.h b/guilib/freetype2/include/freetype/fterrdef.h new file mode 100644 index 0000000000..d7ad256bdb --- /dev/null +++ b/guilib/freetype2/include/freetype/fterrdef.h @@ -0,0 +1,239 @@ +/***************************************************************************/ +/* */ +/* fterrdef.h */ +/* */ +/* FreeType error codes (specification). */ +/* */ +/* Copyright 2002, 2004, 2006, 2007 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** LIST OF ERROR CODES/MESSAGES *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + + + /* You need to define both FT_ERRORDEF_ and FT_NOERRORDEF_ before */ + /* including this file. */ + + + /* generic errors */ + + FT_NOERRORDEF_( Ok, 0x00, \ + "no error" ) + + FT_ERRORDEF_( Cannot_Open_Resource, 0x01, \ + "cannot open resource" ) + FT_ERRORDEF_( Unknown_File_Format, 0x02, \ + "unknown file format" ) + FT_ERRORDEF_( Invalid_File_Format, 0x03, \ + "broken file" ) + FT_ERRORDEF_( Invalid_Version, 0x04, \ + "invalid FreeType version" ) + FT_ERRORDEF_( Lower_Module_Version, 0x05, \ + "module version is too low" ) + FT_ERRORDEF_( Invalid_Argument, 0x06, \ + "invalid argument" ) + FT_ERRORDEF_( Unimplemented_Feature, 0x07, \ + "unimplemented feature" ) + FT_ERRORDEF_( Invalid_Table, 0x08, \ + "broken table" ) + FT_ERRORDEF_( Invalid_Offset, 0x09, \ + "broken offset within table" ) + FT_ERRORDEF_( Array_Too_Large, 0x0A, \ + "array allocation size too large" ) + + /* glyph/character errors */ + + FT_ERRORDEF_( Invalid_Glyph_Index, 0x10, \ + "invalid glyph index" ) + FT_ERRORDEF_( Invalid_Character_Code, 0x11, \ + "invalid character code" ) + FT_ERRORDEF_( Invalid_Glyph_Format, 0x12, \ + "unsupported glyph image format" ) + FT_ERRORDEF_( Cannot_Render_Glyph, 0x13, \ + "cannot render this glyph format" ) + FT_ERRORDEF_( Invalid_Outline, 0x14, \ + "invalid outline" ) + FT_ERRORDEF_( Invalid_Composite, 0x15, \ + "invalid composite glyph" ) + FT_ERRORDEF_( Too_Many_Hints, 0x16, \ + "too many hints" ) + FT_ERRORDEF_( Invalid_Pixel_Size, 0x17, \ + "invalid pixel size" ) + + /* handle errors */ + + FT_ERRORDEF_( Invalid_Handle, 0x20, \ + "invalid object handle" ) + FT_ERRORDEF_( Invalid_Library_Handle, 0x21, \ + "invalid library handle" ) + FT_ERRORDEF_( Invalid_Driver_Handle, 0x22, \ + "invalid module handle" ) + FT_ERRORDEF_( Invalid_Face_Handle, 0x23, \ + "invalid face handle" ) + FT_ERRORDEF_( Invalid_Size_Handle, 0x24, \ + "invalid size handle" ) + FT_ERRORDEF_( Invalid_Slot_Handle, 0x25, \ + "invalid glyph slot handle" ) + FT_ERRORDEF_( Invalid_CharMap_Handle, 0x26, \ + "invalid charmap handle" ) + FT_ERRORDEF_( Invalid_Cache_Handle, 0x27, \ + "invalid cache manager handle" ) + FT_ERRORDEF_( Invalid_Stream_Handle, 0x28, \ + "invalid stream handle" ) + + /* driver errors */ + + FT_ERRORDEF_( Too_Many_Drivers, 0x30, \ + "too many modules" ) + FT_ERRORDEF_( Too_Many_Extensions, 0x31, \ + "too many extensions" ) + + /* memory errors */ + + FT_ERRORDEF_( Out_Of_Memory, 0x40, \ + "out of memory" ) + FT_ERRORDEF_( Unlisted_Object, 0x41, \ + "unlisted object" ) + + /* stream errors */ + + FT_ERRORDEF_( Cannot_Open_Stream, 0x51, \ + "cannot open stream" ) + FT_ERRORDEF_( Invalid_Stream_Seek, 0x52, \ + "invalid stream seek" ) + FT_ERRORDEF_( Invalid_Stream_Skip, 0x53, \ + "invalid stream skip" ) + FT_ERRORDEF_( Invalid_Stream_Read, 0x54, \ + "invalid stream read" ) + FT_ERRORDEF_( Invalid_Stream_Operation, 0x55, \ + "invalid stream operation" ) + FT_ERRORDEF_( Invalid_Frame_Operation, 0x56, \ + "invalid frame operation" ) + FT_ERRORDEF_( Nested_Frame_Access, 0x57, \ + "nested frame access" ) + FT_ERRORDEF_( Invalid_Frame_Read, 0x58, \ + "invalid frame read" ) + + /* raster errors */ + + FT_ERRORDEF_( Raster_Uninitialized, 0x60, \ + "raster uninitialized" ) + FT_ERRORDEF_( Raster_Corrupted, 0x61, \ + "raster corrupted" ) + FT_ERRORDEF_( Raster_Overflow, 0x62, \ + "raster overflow" ) + FT_ERRORDEF_( Raster_Negative_Height, 0x63, \ + "negative height while rastering" ) + + /* cache errors */ + + FT_ERRORDEF_( Too_Many_Caches, 0x70, \ + "too many registered caches" ) + + /* TrueType and SFNT errors */ + + FT_ERRORDEF_( Invalid_Opcode, 0x80, \ + "invalid opcode" ) + FT_ERRORDEF_( Too_Few_Arguments, 0x81, \ + "too few arguments" ) + FT_ERRORDEF_( Stack_Overflow, 0x82, \ + "stack overflow" ) + FT_ERRORDEF_( Code_Overflow, 0x83, \ + "code overflow" ) + FT_ERRORDEF_( Bad_Argument, 0x84, \ + "bad argument" ) + FT_ERRORDEF_( Divide_By_Zero, 0x85, \ + "division by zero" ) + FT_ERRORDEF_( Invalid_Reference, 0x86, \ + "invalid reference" ) + FT_ERRORDEF_( Debug_OpCode, 0x87, \ + "found debug opcode" ) + FT_ERRORDEF_( ENDF_In_Exec_Stream, 0x88, \ + "found ENDF opcode in execution stream" ) + FT_ERRORDEF_( Nested_DEFS, 0x89, \ + "nested DEFS" ) + FT_ERRORDEF_( Invalid_CodeRange, 0x8A, \ + "invalid code range" ) + FT_ERRORDEF_( Execution_Too_Long, 0x8B, \ + "execution context too long" ) + FT_ERRORDEF_( Too_Many_Function_Defs, 0x8C, \ + "too many function definitions" ) + FT_ERRORDEF_( Too_Many_Instruction_Defs, 0x8D, \ + "too many instruction definitions" ) + FT_ERRORDEF_( Table_Missing, 0x8E, \ + "SFNT font table missing" ) + FT_ERRORDEF_( Horiz_Header_Missing, 0x8F, \ + "horizontal header (hhea) table missing" ) + FT_ERRORDEF_( Locations_Missing, 0x90, \ + "locations (loca) table missing" ) + FT_ERRORDEF_( Name_Table_Missing, 0x91, \ + "name table missing" ) + FT_ERRORDEF_( CMap_Table_Missing, 0x92, \ + "character map (cmap) table missing" ) + FT_ERRORDEF_( Hmtx_Table_Missing, 0x93, \ + "horizontal metrics (hmtx) table missing" ) + FT_ERRORDEF_( Post_Table_Missing, 0x94, \ + "PostScript (post) table missing" ) + FT_ERRORDEF_( Invalid_Horiz_Metrics, 0x95, \ + "invalid horizontal metrics" ) + FT_ERRORDEF_( Invalid_CharMap_Format, 0x96, \ + "invalid character map (cmap) format" ) + FT_ERRORDEF_( Invalid_PPem, 0x97, \ + "invalid ppem value" ) + FT_ERRORDEF_( Invalid_Vert_Metrics, 0x98, \ + "invalid vertical metrics" ) + FT_ERRORDEF_( Could_Not_Find_Context, 0x99, \ + "could not find context" ) + FT_ERRORDEF_( Invalid_Post_Table_Format, 0x9A, \ + "invalid PostScript (post) table format" ) + FT_ERRORDEF_( Invalid_Post_Table, 0x9B, \ + "invalid PostScript (post) table" ) + + /* CFF, CID, and Type 1 errors */ + + FT_ERRORDEF_( Syntax_Error, 0xA0, \ + "opcode syntax error" ) + FT_ERRORDEF_( Stack_Underflow, 0xA1, \ + "argument stack underflow" ) + FT_ERRORDEF_( Ignore, 0xA2, \ + "ignore" ) + + /* BDF errors */ + + FT_ERRORDEF_( Missing_Startfont_Field, 0xB0, \ + "`STARTFONT' field missing" ) + FT_ERRORDEF_( Missing_Font_Field, 0xB1, \ + "`FONT' field missing" ) + FT_ERRORDEF_( Missing_Size_Field, 0xB2, \ + "`SIZE' field missing" ) + FT_ERRORDEF_( Missing_Chars_Field, 0xB3, \ + "`CHARS' field missing" ) + FT_ERRORDEF_( Missing_Startchar_Field, 0xB4, \ + "`STARTCHAR' field missing" ) + FT_ERRORDEF_( Missing_Encoding_Field, 0xB5, \ + "`ENCODING' field missing" ) + FT_ERRORDEF_( Missing_Bbx_Field, 0xB6, \ + "`BBX' field missing" ) + FT_ERRORDEF_( Bbx_Too_Big, 0xB7, \ + "`BBX' too big" ) + FT_ERRORDEF_( Corrupted_Font_Header, 0xB8, \ + "Font header corrupted or missing fields" ) + FT_ERRORDEF_( Corrupted_Font_Glyphs, 0xB9, \ + "Font glyphs corrupted or missing fields" ) + + +/* END */ diff --git a/guilib/freetype2/include/freetype/fterrors.h b/guilib/freetype2/include/freetype/fterrors.h new file mode 100644 index 0000000000..6600dadd0d --- /dev/null +++ b/guilib/freetype2/include/freetype/fterrors.h @@ -0,0 +1,206 @@ +/***************************************************************************/ +/* */ +/* fterrors.h */ +/* */ +/* FreeType error code handling (specification). */ +/* */ +/* Copyright 1996-2001, 2002, 2004, 2007 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This special header file is used to define the handling of FT2 */ + /* enumeration constants. It can also be used to generate error message */ + /* strings with a small macro trick explained below. */ + /* */ + /* I - Error Formats */ + /* ----------------- */ + /* */ + /* The configuration macro FT_CONFIG_OPTION_USE_MODULE_ERRORS can be */ + /* defined in ftoption.h in order to make the higher byte indicate */ + /* the module where the error has happened (this is not compatible */ + /* with standard builds of FreeType 2). You can then use the macro */ + /* FT_ERROR_BASE macro to extract the generic error code from an */ + /* FT_Error value. */ + /* */ + /* */ + /* II - Error Message strings */ + /* -------------------------- */ + /* */ + /* The error definitions below are made through special macros that */ + /* allow client applications to build a table of error message strings */ + /* if they need it. The strings are not included in a normal build of */ + /* FreeType 2 to save space (most client applications do not use */ + /* them). */ + /* */ + /* To do so, you have to define the following macros before including */ + /* this file: */ + /* */ + /* FT_ERROR_START_LIST :: */ + /* This macro is called before anything else to define the start of */ + /* the error list. It is followed by several FT_ERROR_DEF calls */ + /* (see below). */ + /* */ + /* FT_ERROR_DEF( e, v, s ) :: */ + /* This macro is called to define one single error. */ + /* `e' is the error code identifier (e.g. FT_Err_Invalid_Argument). */ + /* `v' is the error numerical value. */ + /* `s' is the corresponding error string. */ + /* */ + /* FT_ERROR_END_LIST :: */ + /* This macro ends the list. */ + /* */ + /* Additionally, you have to undefine __FTERRORS_H__ before #including */ + /* this file. */ + /* */ + /* Here is a simple example: */ + /* */ + /* { */ + /* #undef __FTERRORS_H__ */ + /* #define FT_ERRORDEF( e, v, s ) { e, s }, */ + /* #define FT_ERROR_START_LIST { */ + /* #define FT_ERROR_END_LIST { 0, 0 } }; */ + /* */ + /* const struct */ + /* { */ + /* int err_code; */ + /* const char* err_msg; */ + /* } ft_errors[] = */ + /* */ + /* #include FT_ERRORS_H */ + /* } */ + /* */ + /*************************************************************************/ + + +#ifndef __FTERRORS_H__ +#define __FTERRORS_H__ + + + /* include module base error codes */ +#include FT_MODULE_ERRORS_H + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** SETUP MACROS *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + + +#undef FT_NEED_EXTERN_C + +#undef FT_ERR_XCAT +#undef FT_ERR_CAT + +#define FT_ERR_XCAT( x, y ) x ## y +#define FT_ERR_CAT( x, y ) FT_ERR_XCAT( x, y ) + + + /* FT_ERR_PREFIX is used as a prefix for error identifiers. */ + /* By default, we use `FT_Err_'. */ + /* */ +#ifndef FT_ERR_PREFIX +#define FT_ERR_PREFIX FT_Err_ +#endif + + + /* FT_ERR_BASE is used as the base for module-specific errors. */ + /* */ +#ifdef FT_CONFIG_OPTION_USE_MODULE_ERRORS + +#ifndef FT_ERR_BASE +#define FT_ERR_BASE FT_Mod_Err_Base +#endif + +#else + +#undef FT_ERR_BASE +#define FT_ERR_BASE 0 + +#endif /* FT_CONFIG_OPTION_USE_MODULE_ERRORS */ + + + /* If FT_ERRORDEF is not defined, we need to define a simple */ + /* enumeration type. */ + /* */ +#ifndef FT_ERRORDEF + +#define FT_ERRORDEF( e, v, s ) e = v, +#define FT_ERROR_START_LIST enum { +#define FT_ERROR_END_LIST FT_ERR_CAT( FT_ERR_PREFIX, Max ) }; + +#ifdef __cplusplus +#define FT_NEED_EXTERN_C + extern "C" { +#endif + +#endif /* !FT_ERRORDEF */ + + + /* this macro is used to define an error */ +#define FT_ERRORDEF_( e, v, s ) \ + FT_ERRORDEF( FT_ERR_CAT( FT_ERR_PREFIX, e ), v + FT_ERR_BASE, s ) + + /* this is only used for <module>_Err_Ok, which must be 0! */ +#define FT_NOERRORDEF_( e, v, s ) \ + FT_ERRORDEF( FT_ERR_CAT( FT_ERR_PREFIX, e ), v, s ) + + +#ifdef FT_ERROR_START_LIST + FT_ERROR_START_LIST +#endif + + + /* now include the error codes */ +#include FT_ERROR_DEFINITIONS_H + + +#ifdef FT_ERROR_END_LIST + FT_ERROR_END_LIST +#endif + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** SIMPLE CLEANUP *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + +#ifdef FT_NEED_EXTERN_C + } +#endif + +#undef FT_ERROR_START_LIST +#undef FT_ERROR_END_LIST + +#undef FT_ERRORDEF +#undef FT_ERRORDEF_ +#undef FT_NOERRORDEF_ + +#undef FT_NEED_EXTERN_C +#undef FT_ERR_CONCAT +#undef FT_ERR_BASE + + /* FT_KEEP_ERR_PREFIX is needed for ftvalid.h */ +#ifndef FT_KEEP_ERR_PREFIX +#undef FT_ERR_PREFIX +#endif + +#endif /* __FTERRORS_H__ */ + + +/* END */ diff --git a/guilib/freetype2/include/freetype/ftglyph.h b/guilib/freetype2/include/freetype/ftglyph.h new file mode 100644 index 0000000000..cacccf025e --- /dev/null +++ b/guilib/freetype2/include/freetype/ftglyph.h @@ -0,0 +1,613 @@ +/***************************************************************************/ +/* */ +/* ftglyph.h */ +/* */ +/* FreeType convenience functions to handle glyphs (specification). */ +/* */ +/* Copyright 1996-2001, 2002, 2003, 2006, 2008, 2009 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file contains the definition of several convenience functions */ + /* that can be used by client applications to easily retrieve glyph */ + /* bitmaps and outlines from a given face. */ + /* */ + /* These functions should be optional if you are writing a font server */ + /* or text layout engine on top of FreeType. However, they are pretty */ + /* handy for many other simple uses of the library. */ + /* */ + /*************************************************************************/ + + +#ifndef __FTGLYPH_H__ +#define __FTGLYPH_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* glyph_management */ + /* */ + /* <Title> */ + /* Glyph Management */ + /* */ + /* <Abstract> */ + /* Generic interface to manage individual glyph data. */ + /* */ + /* <Description> */ + /* This section contains definitions used to manage glyph data */ + /* through generic FT_Glyph objects. Each of them can contain a */ + /* bitmap, a vector outline, or even images in other formats. */ + /* */ + /*************************************************************************/ + + + /* forward declaration to a private type */ + typedef struct FT_Glyph_Class_ FT_Glyph_Class; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Glyph */ + /* */ + /* <Description> */ + /* Handle to an object used to model generic glyph images. It is a */ + /* pointer to the @FT_GlyphRec structure and can contain a glyph */ + /* bitmap or pointer. */ + /* */ + /* <Note> */ + /* Glyph objects are not owned by the library. You must thus release */ + /* them manually (through @FT_Done_Glyph) _before_ calling */ + /* @FT_Done_FreeType. */ + /* */ + typedef struct FT_GlyphRec_* FT_Glyph; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_GlyphRec */ + /* */ + /* <Description> */ + /* The root glyph structure contains a given glyph image plus its */ + /* advance width in 16.16 fixed float format. */ + /* */ + /* <Fields> */ + /* library :: A handle to the FreeType library object. */ + /* */ + /* clazz :: A pointer to the glyph's class. Private. */ + /* */ + /* format :: The format of the glyph's image. */ + /* */ + /* advance :: A 16.16 vector that gives the glyph's advance width. */ + /* */ + typedef struct FT_GlyphRec_ + { + FT_Library library; + const FT_Glyph_Class* clazz; + FT_Glyph_Format format; + FT_Vector advance; + + } FT_GlyphRec; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_BitmapGlyph */ + /* */ + /* <Description> */ + /* A handle to an object used to model a bitmap glyph image. This is */ + /* a sub-class of @FT_Glyph, and a pointer to @FT_BitmapGlyphRec. */ + /* */ + typedef struct FT_BitmapGlyphRec_* FT_BitmapGlyph; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_BitmapGlyphRec */ + /* */ + /* <Description> */ + /* A structure used for bitmap glyph images. This really is a */ + /* `sub-class' of @FT_GlyphRec. */ + /* */ + /* <Fields> */ + /* root :: The root @FT_Glyph fields. */ + /* */ + /* left :: The left-side bearing, i.e., the horizontal distance */ + /* from the current pen position to the left border of the */ + /* glyph bitmap. */ + /* */ + /* top :: The top-side bearing, i.e., the vertical distance from */ + /* the current pen position to the top border of the glyph */ + /* bitmap. This distance is positive for upwards~y! */ + /* */ + /* bitmap :: A descriptor for the bitmap. */ + /* */ + /* <Note> */ + /* You can typecast an @FT_Glyph to @FT_BitmapGlyph if you have */ + /* `glyph->format == FT_GLYPH_FORMAT_BITMAP'. This lets you access */ + /* the bitmap's contents easily. */ + /* */ + /* The corresponding pixel buffer is always owned by @FT_BitmapGlyph */ + /* and is thus created and destroyed with it. */ + /* */ + typedef struct FT_BitmapGlyphRec_ + { + FT_GlyphRec root; + FT_Int left; + FT_Int top; + FT_Bitmap bitmap; + + } FT_BitmapGlyphRec; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_OutlineGlyph */ + /* */ + /* <Description> */ + /* A handle to an object used to model an outline glyph image. This */ + /* is a sub-class of @FT_Glyph, and a pointer to @FT_OutlineGlyphRec. */ + /* */ + typedef struct FT_OutlineGlyphRec_* FT_OutlineGlyph; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_OutlineGlyphRec */ + /* */ + /* <Description> */ + /* A structure used for outline (vectorial) glyph images. This */ + /* really is a `sub-class' of @FT_GlyphRec. */ + /* */ + /* <Fields> */ + /* root :: The root @FT_Glyph fields. */ + /* */ + /* outline :: A descriptor for the outline. */ + /* */ + /* <Note> */ + /* You can typecast an @FT_Glyph to @FT_OutlineGlyph if you have */ + /* `glyph->format == FT_GLYPH_FORMAT_OUTLINE'. This lets you access */ + /* the outline's content easily. */ + /* */ + /* As the outline is extracted from a glyph slot, its coordinates are */ + /* expressed normally in 26.6 pixels, unless the flag */ + /* @FT_LOAD_NO_SCALE was used in @FT_Load_Glyph() or @FT_Load_Char(). */ + /* */ + /* The outline's tables are always owned by the object and are */ + /* destroyed with it. */ + /* */ + typedef struct FT_OutlineGlyphRec_ + { + FT_GlyphRec root; + FT_Outline outline; + + } FT_OutlineGlyphRec; + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Get_Glyph */ + /* */ + /* <Description> */ + /* A function used to extract a glyph image from a slot. Note that */ + /* the created @FT_Glyph object must be released with @FT_Done_Glyph. */ + /* */ + /* <Input> */ + /* slot :: A handle to the source glyph slot. */ + /* */ + /* <Output> */ + /* aglyph :: A handle to the glyph object. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Glyph( FT_GlyphSlot slot, + FT_Glyph *aglyph ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Glyph_Copy */ + /* */ + /* <Description> */ + /* A function used to copy a glyph image. Note that the created */ + /* @FT_Glyph object must be released with @FT_Done_Glyph. */ + /* */ + /* <Input> */ + /* source :: A handle to the source glyph object. */ + /* */ + /* <Output> */ + /* target :: A handle to the target glyph object. 0~in case of */ + /* error. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Glyph_Copy( FT_Glyph source, + FT_Glyph *target ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Glyph_Transform */ + /* */ + /* <Description> */ + /* Transform a glyph image if its format is scalable. */ + /* */ + /* <InOut> */ + /* glyph :: A handle to the target glyph object. */ + /* */ + /* <Input> */ + /* matrix :: A pointer to a 2x2 matrix to apply. */ + /* */ + /* delta :: A pointer to a 2d vector to apply. Coordinates are */ + /* expressed in 1/64th of a pixel. */ + /* */ + /* <Return> */ + /* FreeType error code (if not 0, the glyph format is not scalable). */ + /* */ + /* <Note> */ + /* The 2x2 transformation matrix is also applied to the glyph's */ + /* advance vector. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Glyph_Transform( FT_Glyph glyph, + FT_Matrix* matrix, + FT_Vector* delta ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Glyph_BBox_Mode */ + /* */ + /* <Description> */ + /* The mode how the values of @FT_Glyph_Get_CBox are returned. */ + /* */ + /* <Values> */ + /* FT_GLYPH_BBOX_UNSCALED :: */ + /* Return unscaled font units. */ + /* */ + /* FT_GLYPH_BBOX_SUBPIXELS :: */ + /* Return unfitted 26.6 coordinates. */ + /* */ + /* FT_GLYPH_BBOX_GRIDFIT :: */ + /* Return grid-fitted 26.6 coordinates. */ + /* */ + /* FT_GLYPH_BBOX_TRUNCATE :: */ + /* Return coordinates in integer pixels. */ + /* */ + /* FT_GLYPH_BBOX_PIXELS :: */ + /* Return grid-fitted pixel coordinates. */ + /* */ + typedef enum FT_Glyph_BBox_Mode_ + { + FT_GLYPH_BBOX_UNSCALED = 0, + FT_GLYPH_BBOX_SUBPIXELS = 0, + FT_GLYPH_BBOX_GRIDFIT = 1, + FT_GLYPH_BBOX_TRUNCATE = 2, + FT_GLYPH_BBOX_PIXELS = 3 + + } FT_Glyph_BBox_Mode; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* ft_glyph_bbox_xxx */ + /* */ + /* <Description> */ + /* These constants are deprecated. Use the corresponding */ + /* @FT_Glyph_BBox_Mode values instead. */ + /* */ + /* <Values> */ + /* ft_glyph_bbox_unscaled :: See @FT_GLYPH_BBOX_UNSCALED. */ + /* ft_glyph_bbox_subpixels :: See @FT_GLYPH_BBOX_SUBPIXELS. */ + /* ft_glyph_bbox_gridfit :: See @FT_GLYPH_BBOX_GRIDFIT. */ + /* ft_glyph_bbox_truncate :: See @FT_GLYPH_BBOX_TRUNCATE. */ + /* ft_glyph_bbox_pixels :: See @FT_GLYPH_BBOX_PIXELS. */ + /* */ +#define ft_glyph_bbox_unscaled FT_GLYPH_BBOX_UNSCALED +#define ft_glyph_bbox_subpixels FT_GLYPH_BBOX_SUBPIXELS +#define ft_glyph_bbox_gridfit FT_GLYPH_BBOX_GRIDFIT +#define ft_glyph_bbox_truncate FT_GLYPH_BBOX_TRUNCATE +#define ft_glyph_bbox_pixels FT_GLYPH_BBOX_PIXELS + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Glyph_Get_CBox */ + /* */ + /* <Description> */ + /* Return a glyph's `control box'. The control box encloses all the */ + /* outline's points, including Bézier control points. Though it */ + /* coincides with the exact bounding box for most glyphs, it can be */ + /* slightly larger in some situations (like when rotating an outline */ + /* which contains Bézier outside arcs). */ + /* */ + /* Computing the control box is very fast, while getting the bounding */ + /* box can take much more time as it needs to walk over all segments */ + /* and arcs in the outline. To get the latter, you can use the */ + /* `ftbbox' component which is dedicated to this single task. */ + /* */ + /* <Input> */ + /* glyph :: A handle to the source glyph object. */ + /* */ + /* mode :: The mode which indicates how to interpret the returned */ + /* bounding box values. */ + /* */ + /* <Output> */ + /* acbox :: The glyph coordinate bounding box. Coordinates are */ + /* expressed in 1/64th of pixels if it is grid-fitted. */ + /* */ + /* <Note> */ + /* Coordinates are relative to the glyph origin, using the y~upwards */ + /* convention. */ + /* */ + /* If the glyph has been loaded with @FT_LOAD_NO_SCALE, `bbox_mode' */ + /* must be set to @FT_GLYPH_BBOX_UNSCALED to get unscaled font */ + /* units in 26.6 pixel format. The value @FT_GLYPH_BBOX_SUBPIXELS */ + /* is another name for this constant. */ + /* */ + /* Note that the maximum coordinates are exclusive, which means that */ + /* one can compute the width and height of the glyph image (be it in */ + /* integer or 26.6 pixels) as: */ + /* */ + /* { */ + /* width = bbox.xMax - bbox.xMin; */ + /* height = bbox.yMax - bbox.yMin; */ + /* } */ + /* */ + /* Note also that for 26.6 coordinates, if `bbox_mode' is set to */ + /* @FT_GLYPH_BBOX_GRIDFIT, the coordinates will also be grid-fitted, */ + /* which corresponds to: */ + /* */ + /* { */ + /* bbox.xMin = FLOOR(bbox.xMin); */ + /* bbox.yMin = FLOOR(bbox.yMin); */ + /* bbox.xMax = CEILING(bbox.xMax); */ + /* bbox.yMax = CEILING(bbox.yMax); */ + /* } */ + /* */ + /* To get the bbox in pixel coordinates, set `bbox_mode' to */ + /* @FT_GLYPH_BBOX_TRUNCATE. */ + /* */ + /* To get the bbox in grid-fitted pixel coordinates, set `bbox_mode' */ + /* to @FT_GLYPH_BBOX_PIXELS. */ + /* */ + FT_EXPORT( void ) + FT_Glyph_Get_CBox( FT_Glyph glyph, + FT_UInt bbox_mode, + FT_BBox *acbox ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Glyph_To_Bitmap */ + /* */ + /* <Description> */ + /* Convert a given glyph object to a bitmap glyph object. */ + /* */ + /* <InOut> */ + /* the_glyph :: A pointer to a handle to the target glyph. */ + /* */ + /* <Input> */ + /* render_mode :: An enumeration that describes how the data is */ + /* rendered. */ + /* */ + /* origin :: A pointer to a vector used to translate the glyph */ + /* image before rendering. Can be~0 (if no */ + /* translation). The origin is expressed in */ + /* 26.6 pixels. */ + /* */ + /* destroy :: A boolean that indicates that the original glyph */ + /* image should be destroyed by this function. It is */ + /* never destroyed in case of error. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* This function does nothing if the glyph format isn't scalable. */ + /* */ + /* The glyph image is translated with the `origin' vector before */ + /* rendering. */ + /* */ + /* The first parameter is a pointer to an @FT_Glyph handle, that will */ + /* be _replaced_ by this function (with newly allocated data). */ + /* Typically, you would use (omitting error handling): */ + /* */ + /* */ + /* { */ + /* FT_Glyph glyph; */ + /* FT_BitmapGlyph glyph_bitmap; */ + /* */ + /* */ + /* // load glyph */ + /* error = FT_Load_Char( face, glyph_index, FT_LOAD_DEFAUT ); */ + /* */ + /* // extract glyph image */ + /* error = FT_Get_Glyph( face->glyph, &glyph ); */ + /* */ + /* // convert to a bitmap (default render mode + destroying old) */ + /* if ( glyph->format != FT_GLYPH_FORMAT_BITMAP ) */ + /* { */ + /* error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_DEFAULT, */ + /* 0, 1 ); */ + /* if ( error ) // `glyph' unchanged */ + /* ... */ + /* } */ + /* */ + /* // access bitmap content by typecasting */ + /* glyph_bitmap = (FT_BitmapGlyph)glyph; */ + /* */ + /* // do funny stuff with it, like blitting/drawing */ + /* ... */ + /* */ + /* // discard glyph image (bitmap or not) */ + /* FT_Done_Glyph( glyph ); */ + /* } */ + /* */ + /* */ + /* Here another example, again without error handling: */ + /* */ + /* */ + /* { */ + /* FT_Glyph glyphs[MAX_GLYPHS] */ + /* */ + /* */ + /* ... */ + /* */ + /* for ( idx = 0; i < MAX_GLYPHS; i++ ) */ + /* error = FT_Load_Glyph( face, idx, FT_LOAD_DEFAULT ) || */ + /* FT_Get_Glyph ( face->glyph, &glyph[idx] ); */ + /* */ + /* ... */ + /* */ + /* for ( idx = 0; i < MAX_GLYPHS; i++ ) */ + /* { */ + /* FT_Glyph bitmap = glyphs[idx]; */ + /* */ + /* */ + /* ... */ + /* */ + /* // after this call, `bitmap' no longer points into */ + /* // the `glyphs' array (and the old value isn't destroyed) */ + /* FT_Glyph_To_Bitmap( &bitmap, FT_RENDER_MODE_MONO, 0, 0 ); */ + /* */ + /* ... */ + /* */ + /* FT_Done_Glyph( bitmap ); */ + /* } */ + /* */ + /* ... */ + /* */ + /* for ( idx = 0; i < MAX_GLYPHS; i++ ) */ + /* FT_Done_Glyph( glyphs[idx] ); */ + /* } */ + /* */ + FT_EXPORT( FT_Error ) + FT_Glyph_To_Bitmap( FT_Glyph* the_glyph, + FT_Render_Mode render_mode, + FT_Vector* origin, + FT_Bool destroy ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Done_Glyph */ + /* */ + /* <Description> */ + /* Destroy a given glyph. */ + /* */ + /* <Input> */ + /* glyph :: A handle to the target glyph object. */ + /* */ + FT_EXPORT( void ) + FT_Done_Glyph( FT_Glyph glyph ); + + /* */ + + + /* other helpful functions */ + + /*************************************************************************/ + /* */ + /* <Section> */ + /* computations */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Matrix_Multiply */ + /* */ + /* <Description> */ + /* Perform the matrix operation `b = a*b'. */ + /* */ + /* <Input> */ + /* a :: A pointer to matrix `a'. */ + /* */ + /* <InOut> */ + /* b :: A pointer to matrix `b'. */ + /* */ + /* <Note> */ + /* The result is undefined if either `a' or `b' is zero. */ + /* */ + FT_EXPORT( void ) + FT_Matrix_Multiply( const FT_Matrix* a, + FT_Matrix* b ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Matrix_Invert */ + /* */ + /* <Description> */ + /* Invert a 2x2 matrix. Return an error if it can't be inverted. */ + /* */ + /* <InOut> */ + /* matrix :: A pointer to the target matrix. Remains untouched in */ + /* case of error. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Matrix_Invert( FT_Matrix* matrix ); + + + /* */ + + +FT_END_HEADER + +#endif /* __FTGLYPH_H__ */ + + +/* END */ + + +/* Local Variables: */ +/* coding: utf-8 */ +/* End: */ diff --git a/guilib/freetype2/include/freetype/ftimage.h b/guilib/freetype2/include/freetype/ftimage.h new file mode 100644 index 0000000000..ccc2f71350 --- /dev/null +++ b/guilib/freetype2/include/freetype/ftimage.h @@ -0,0 +1,1254 @@ +/***************************************************************************/ +/* */ +/* ftimage.h */ +/* */ +/* FreeType glyph image formats and default raster interface */ +/* (specification). */ +/* */ +/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* Note: A `raster' is simply a scan-line converter, used to render */ + /* FT_Outlines into FT_Bitmaps. */ + /* */ + /*************************************************************************/ + + +#ifndef __FTIMAGE_H__ +#define __FTIMAGE_H__ + + + /* _STANDALONE_ is from ftgrays.c */ +#ifndef _STANDALONE_ +#include <ft2build.h> +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* basic_types */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Pos */ + /* */ + /* <Description> */ + /* The type FT_Pos is a 32-bit integer used to store vectorial */ + /* coordinates. Depending on the context, these can represent */ + /* distances in integer font units, or 16.16, or 26.6 fixed float */ + /* pixel coordinates. */ + /* */ + typedef signed long FT_Pos; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Vector */ + /* */ + /* <Description> */ + /* A simple structure used to store a 2D vector; coordinates are of */ + /* the FT_Pos type. */ + /* */ + /* <Fields> */ + /* x :: The horizontal coordinate. */ + /* y :: The vertical coordinate. */ + /* */ + typedef struct FT_Vector_ + { + FT_Pos x; + FT_Pos y; + + } FT_Vector; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_BBox */ + /* */ + /* <Description> */ + /* A structure used to hold an outline's bounding box, i.e., the */ + /* coordinates of its extrema in the horizontal and vertical */ + /* directions. */ + /* */ + /* <Fields> */ + /* xMin :: The horizontal minimum (left-most). */ + /* */ + /* yMin :: The vertical minimum (bottom-most). */ + /* */ + /* xMax :: The horizontal maximum (right-most). */ + /* */ + /* yMax :: The vertical maximum (top-most). */ + /* */ + typedef struct FT_BBox_ + { + FT_Pos xMin, yMin; + FT_Pos xMax, yMax; + + } FT_BBox; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Pixel_Mode */ + /* */ + /* <Description> */ + /* An enumeration type used to describe the format of pixels in a */ + /* given bitmap. Note that additional formats may be added in the */ + /* future. */ + /* */ + /* <Values> */ + /* FT_PIXEL_MODE_NONE :: */ + /* Value~0 is reserved. */ + /* */ + /* FT_PIXEL_MODE_MONO :: */ + /* A monochrome bitmap, using 1~bit per pixel. Note that pixels */ + /* are stored in most-significant order (MSB), which means that */ + /* the left-most pixel in a byte has value 128. */ + /* */ + /* FT_PIXEL_MODE_GRAY :: */ + /* An 8-bit bitmap, generally used to represent anti-aliased glyph */ + /* images. Each pixel is stored in one byte. Note that the number */ + /* of `gray' levels is stored in the `num_grays' field of the */ + /* @FT_Bitmap structure (it generally is 256). */ + /* */ + /* FT_PIXEL_MODE_GRAY2 :: */ + /* A 2-bit per pixel bitmap, used to represent embedded */ + /* anti-aliased bitmaps in font files according to the OpenType */ + /* specification. We haven't found a single font using this */ + /* format, however. */ + /* */ + /* FT_PIXEL_MODE_GRAY4 :: */ + /* A 4-bit per pixel bitmap, representing embedded anti-aliased */ + /* bitmaps in font files according to the OpenType specification. */ + /* We haven't found a single font using this format, however. */ + /* */ + /* FT_PIXEL_MODE_LCD :: */ + /* An 8-bit bitmap, representing RGB or BGR decimated glyph images */ + /* used for display on LCD displays; the bitmap is three times */ + /* wider than the original glyph image. See also */ + /* @FT_RENDER_MODE_LCD. */ + /* */ + /* FT_PIXEL_MODE_LCD_V :: */ + /* An 8-bit bitmap, representing RGB or BGR decimated glyph images */ + /* used for display on rotated LCD displays; the bitmap is three */ + /* times taller than the original glyph image. See also */ + /* @FT_RENDER_MODE_LCD_V. */ + /* */ + typedef enum FT_Pixel_Mode_ + { + FT_PIXEL_MODE_NONE = 0, + FT_PIXEL_MODE_MONO, + FT_PIXEL_MODE_GRAY, + FT_PIXEL_MODE_GRAY2, + FT_PIXEL_MODE_GRAY4, + FT_PIXEL_MODE_LCD, + FT_PIXEL_MODE_LCD_V, + + FT_PIXEL_MODE_MAX /* do not remove */ + + } FT_Pixel_Mode; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* ft_pixel_mode_xxx */ + /* */ + /* <Description> */ + /* A list of deprecated constants. Use the corresponding */ + /* @FT_Pixel_Mode values instead. */ + /* */ + /* <Values> */ + /* ft_pixel_mode_none :: See @FT_PIXEL_MODE_NONE. */ + /* ft_pixel_mode_mono :: See @FT_PIXEL_MODE_MONO. */ + /* ft_pixel_mode_grays :: See @FT_PIXEL_MODE_GRAY. */ + /* ft_pixel_mode_pal2 :: See @FT_PIXEL_MODE_GRAY2. */ + /* ft_pixel_mode_pal4 :: See @FT_PIXEL_MODE_GRAY4. */ + /* */ +#define ft_pixel_mode_none FT_PIXEL_MODE_NONE +#define ft_pixel_mode_mono FT_PIXEL_MODE_MONO +#define ft_pixel_mode_grays FT_PIXEL_MODE_GRAY +#define ft_pixel_mode_pal2 FT_PIXEL_MODE_GRAY2 +#define ft_pixel_mode_pal4 FT_PIXEL_MODE_GRAY4 + + /* */ + +#if 0 + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Palette_Mode */ + /* */ + /* <Description> */ + /* THIS TYPE IS DEPRECATED. DO NOT USE IT! */ + /* */ + /* An enumeration type to describe the format of a bitmap palette, */ + /* used with ft_pixel_mode_pal4 and ft_pixel_mode_pal8. */ + /* */ + /* <Values> */ + /* ft_palette_mode_rgb :: The palette is an array of 3-byte RGB */ + /* records. */ + /* */ + /* ft_palette_mode_rgba :: The palette is an array of 4-byte RGBA */ + /* records. */ + /* */ + /* <Note> */ + /* As ft_pixel_mode_pal2, pal4 and pal8 are currently unused by */ + /* FreeType, these types are not handled by the library itself. */ + /* */ + typedef enum FT_Palette_Mode_ + { + ft_palette_mode_rgb = 0, + ft_palette_mode_rgba, + + ft_palette_mode_max /* do not remove */ + + } FT_Palette_Mode; + + /* */ + +#endif + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Bitmap */ + /* */ + /* <Description> */ + /* A structure used to describe a bitmap or pixmap to the raster. */ + /* Note that we now manage pixmaps of various depths through the */ + /* `pixel_mode' field. */ + /* */ + /* <Fields> */ + /* rows :: The number of bitmap rows. */ + /* */ + /* width :: The number of pixels in bitmap row. */ + /* */ + /* pitch :: The pitch's absolute value is the number of bytes */ + /* taken by one bitmap row, including padding. */ + /* However, the pitch is positive when the bitmap has */ + /* a `down' flow, and negative when it has an `up' */ + /* flow. In all cases, the pitch is an offset to add */ + /* to a bitmap pointer in order to go down one row. */ + /* */ + /* buffer :: A typeless pointer to the bitmap buffer. This */ + /* value should be aligned on 32-bit boundaries in */ + /* most cases. */ + /* */ + /* num_grays :: This field is only used with */ + /* @FT_PIXEL_MODE_GRAY; it gives the number of gray */ + /* levels used in the bitmap. */ + /* */ + /* pixel_mode :: The pixel mode, i.e., how pixel bits are stored. */ + /* See @FT_Pixel_Mode for possible values. */ + /* */ + /* palette_mode :: This field is intended for paletted pixel modes; */ + /* it indicates how the palette is stored. Not */ + /* used currently. */ + /* */ + /* palette :: A typeless pointer to the bitmap palette; this */ + /* field is intended for paletted pixel modes. Not */ + /* used currently. */ + /* */ + /* <Note> */ + /* For now, the only pixel modes supported by FreeType are mono and */ + /* grays. However, drivers might be added in the future to support */ + /* more `colorful' options. */ + /* */ + typedef struct FT_Bitmap_ + { + int rows; + int width; + int pitch; + unsigned char* buffer; + short num_grays; + char pixel_mode; + char palette_mode; + void* palette; + + } FT_Bitmap; + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* outline_processing */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Outline */ + /* */ + /* <Description> */ + /* This structure is used to describe an outline to the scan-line */ + /* converter. */ + /* */ + /* <Fields> */ + /* n_contours :: The number of contours in the outline. */ + /* */ + /* n_points :: The number of points in the outline. */ + /* */ + /* points :: A pointer to an array of `n_points' @FT_Vector */ + /* elements, giving the outline's point coordinates. */ + /* */ + /* tags :: A pointer to an array of `n_points' chars, giving */ + /* each outline point's type. If bit~0 is unset, the */ + /* point is `off' the curve, i.e., a Bézier control */ + /* point, while it is `on' when set. */ + /* */ + /* Bit~1 is meaningful for `off' points only. If set, */ + /* it indicates a third-order Bézier arc control point; */ + /* and a second-order control point if unset. */ + /* */ + /* contours :: An array of `n_contours' shorts, giving the end */ + /* point of each contour within the outline. For */ + /* example, the first contour is defined by the points */ + /* `0' to `contours[0]', the second one is defined by */ + /* the points `contours[0]+1' to `contours[1]', etc. */ + /* */ + /* flags :: A set of bit flags used to characterize the outline */ + /* and give hints to the scan-converter and hinter on */ + /* how to convert/grid-fit it. See @FT_OUTLINE_FLAGS. */ + /* */ + typedef struct FT_Outline_ + { + short n_contours; /* number of contours in glyph */ + short n_points; /* number of points in the glyph */ + + FT_Vector* points; /* the outline's points */ + char* tags; /* the points flags */ + short* contours; /* the contour end points */ + + int flags; /* outline masks */ + + } FT_Outline; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_OUTLINE_FLAGS */ + /* */ + /* <Description> */ + /* A list of bit-field constants use for the flags in an outline's */ + /* `flags' field. */ + /* */ + /* <Values> */ + /* FT_OUTLINE_NONE :: */ + /* Value~0 is reserved. */ + /* */ + /* FT_OUTLINE_OWNER :: */ + /* If set, this flag indicates that the outline's field arrays */ + /* (i.e., `points', `flags', and `contours') are `owned' by the */ + /* outline object, and should thus be freed when it is destroyed. */ + /* */ + /* FT_OUTLINE_EVEN_ODD_FILL :: */ + /* By default, outlines are filled using the non-zero winding rule. */ + /* If set to 1, the outline will be filled using the even-odd fill */ + /* rule (only works with the smooth raster). */ + /* */ + /* FT_OUTLINE_REVERSE_FILL :: */ + /* By default, outside contours of an outline are oriented in */ + /* clock-wise direction, as defined in the TrueType specification. */ + /* This flag is set if the outline uses the opposite direction */ + /* (typically for Type~1 fonts). This flag is ignored by the scan */ + /* converter. */ + /* */ + /* FT_OUTLINE_IGNORE_DROPOUTS :: */ + /* By default, the scan converter will try to detect drop-outs in */ + /* an outline and correct the glyph bitmap to ensure consistent */ + /* shape continuity. If set, this flag hints the scan-line */ + /* converter to ignore such cases. */ + /* */ + /* FT_OUTLINE_SMART_DROPOUTS :: */ + /* Select smart dropout control. If unset, use simple dropout */ + /* control. Ignored if @FT_OUTLINE_IGNORE_DROPOUTS is set. */ + /* */ + /* FT_OUTLINE_INCLUDE_STUBS :: */ + /* If set, turn pixels on for `stubs', otherwise exclude them. */ + /* Ignored if @FT_OUTLINE_IGNORE_DROPOUTS is set. */ + /* */ + /* FT_OUTLINE_HIGH_PRECISION :: */ + /* This flag indicates that the scan-line converter should try to */ + /* convert this outline to bitmaps with the highest possible */ + /* quality. It is typically set for small character sizes. Note */ + /* that this is only a hint that might be completely ignored by a */ + /* given scan-converter. */ + /* */ + /* FT_OUTLINE_SINGLE_PASS :: */ + /* This flag is set to force a given scan-converter to only use a */ + /* single pass over the outline to render a bitmap glyph image. */ + /* Normally, it is set for very large character sizes. It is only */ + /* a hint that might be completely ignored by a given */ + /* scan-converter. */ + /* */ + /* <Note> */ + /* Please refer to the description of the `SCANTYPE' instruction in */ + /* the OpenType specification (in file `ttinst1.doc') how simple */ + /* drop-outs, smart drop-outs, and stubs are defined. */ + /* */ +#define FT_OUTLINE_NONE 0x0 +#define FT_OUTLINE_OWNER 0x1 +#define FT_OUTLINE_EVEN_ODD_FILL 0x2 +#define FT_OUTLINE_REVERSE_FILL 0x4 +#define FT_OUTLINE_IGNORE_DROPOUTS 0x8 +#define FT_OUTLINE_SMART_DROPOUTS 0x10 +#define FT_OUTLINE_INCLUDE_STUBS 0x20 + +#define FT_OUTLINE_HIGH_PRECISION 0x100 +#define FT_OUTLINE_SINGLE_PASS 0x200 + + + /************************************************************************* + * + * @enum: + * ft_outline_flags + * + * @description: + * These constants are deprecated. Please use the corresponding + * @FT_OUTLINE_FLAGS values. + * + * @values: + * ft_outline_none :: See @FT_OUTLINE_NONE. + * ft_outline_owner :: See @FT_OUTLINE_OWNER. + * ft_outline_even_odd_fill :: See @FT_OUTLINE_EVEN_ODD_FILL. + * ft_outline_reverse_fill :: See @FT_OUTLINE_REVERSE_FILL. + * ft_outline_ignore_dropouts :: See @FT_OUTLINE_IGNORE_DROPOUTS. + * ft_outline_high_precision :: See @FT_OUTLINE_HIGH_PRECISION. + * ft_outline_single_pass :: See @FT_OUTLINE_SINGLE_PASS. + */ +#define ft_outline_none FT_OUTLINE_NONE +#define ft_outline_owner FT_OUTLINE_OWNER +#define ft_outline_even_odd_fill FT_OUTLINE_EVEN_ODD_FILL +#define ft_outline_reverse_fill FT_OUTLINE_REVERSE_FILL +#define ft_outline_ignore_dropouts FT_OUTLINE_IGNORE_DROPOUTS +#define ft_outline_high_precision FT_OUTLINE_HIGH_PRECISION +#define ft_outline_single_pass FT_OUTLINE_SINGLE_PASS + + /* */ + +#define FT_CURVE_TAG( flag ) ( flag & 3 ) + +#define FT_CURVE_TAG_ON 1 +#define FT_CURVE_TAG_CONIC 0 +#define FT_CURVE_TAG_CUBIC 2 + +#define FT_CURVE_TAG_TOUCH_X 8 /* reserved for the TrueType hinter */ +#define FT_CURVE_TAG_TOUCH_Y 16 /* reserved for the TrueType hinter */ + +#define FT_CURVE_TAG_TOUCH_BOTH ( FT_CURVE_TAG_TOUCH_X | \ + FT_CURVE_TAG_TOUCH_Y ) + +#define FT_Curve_Tag_On FT_CURVE_TAG_ON +#define FT_Curve_Tag_Conic FT_CURVE_TAG_CONIC +#define FT_Curve_Tag_Cubic FT_CURVE_TAG_CUBIC +#define FT_Curve_Tag_Touch_X FT_CURVE_TAG_TOUCH_X +#define FT_Curve_Tag_Touch_Y FT_CURVE_TAG_TOUCH_Y + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Outline_MoveToFunc */ + /* */ + /* <Description> */ + /* A function pointer type used to describe the signature of a `move */ + /* to' function during outline walking/decomposition. */ + /* */ + /* A `move to' is emitted to start a new contour in an outline. */ + /* */ + /* <Input> */ + /* to :: A pointer to the target point of the `move to'. */ + /* */ + /* user :: A typeless pointer which is passed from the caller of the */ + /* decomposition function. */ + /* */ + /* <Return> */ + /* Error code. 0~means success. */ + /* */ + typedef int + (*FT_Outline_MoveToFunc)( const FT_Vector* to, + void* user ); + +#define FT_Outline_MoveTo_Func FT_Outline_MoveToFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Outline_LineToFunc */ + /* */ + /* <Description> */ + /* A function pointer type used to describe the signature of a `line */ + /* to' function during outline walking/decomposition. */ + /* */ + /* A `line to' is emitted to indicate a segment in the outline. */ + /* */ + /* <Input> */ + /* to :: A pointer to the target point of the `line to'. */ + /* */ + /* user :: A typeless pointer which is passed from the caller of the */ + /* decomposition function. */ + /* */ + /* <Return> */ + /* Error code. 0~means success. */ + /* */ + typedef int + (*FT_Outline_LineToFunc)( const FT_Vector* to, + void* user ); + +#define FT_Outline_LineTo_Func FT_Outline_LineToFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Outline_ConicToFunc */ + /* */ + /* <Description> */ + /* A function pointer type use to describe the signature of a `conic */ + /* to' function during outline walking/decomposition. */ + /* */ + /* A `conic to' is emitted to indicate a second-order Bézier arc in */ + /* the outline. */ + /* */ + /* <Input> */ + /* control :: An intermediate control point between the last position */ + /* and the new target in `to'. */ + /* */ + /* to :: A pointer to the target end point of the conic arc. */ + /* */ + /* user :: A typeless pointer which is passed from the caller of */ + /* the decomposition function. */ + /* */ + /* <Return> */ + /* Error code. 0~means success. */ + /* */ + typedef int + (*FT_Outline_ConicToFunc)( const FT_Vector* control, + const FT_Vector* to, + void* user ); + +#define FT_Outline_ConicTo_Func FT_Outline_ConicToFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Outline_CubicToFunc */ + /* */ + /* <Description> */ + /* A function pointer type used to describe the signature of a `cubic */ + /* to' function during outline walking/decomposition. */ + /* */ + /* A `cubic to' is emitted to indicate a third-order Bézier arc. */ + /* */ + /* <Input> */ + /* control1 :: A pointer to the first Bézier control point. */ + /* */ + /* control2 :: A pointer to the second Bézier control point. */ + /* */ + /* to :: A pointer to the target end point. */ + /* */ + /* user :: A typeless pointer which is passed from the caller of */ + /* the decomposition function. */ + /* */ + /* <Return> */ + /* Error code. 0~means success. */ + /* */ + typedef int + (*FT_Outline_CubicToFunc)( const FT_Vector* control1, + const FT_Vector* control2, + const FT_Vector* to, + void* user ); + +#define FT_Outline_CubicTo_Func FT_Outline_CubicToFunc + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Outline_Funcs */ + /* */ + /* <Description> */ + /* A structure to hold various function pointers used during outline */ + /* decomposition in order to emit segments, conic, and cubic Béziers, */ + /* as well as `move to' and `close to' operations. */ + /* */ + /* <Fields> */ + /* move_to :: The `move to' emitter. */ + /* */ + /* line_to :: The segment emitter. */ + /* */ + /* conic_to :: The second-order Bézier arc emitter. */ + /* */ + /* cubic_to :: The third-order Bézier arc emitter. */ + /* */ + /* shift :: The shift that is applied to coordinates before they */ + /* are sent to the emitter. */ + /* */ + /* delta :: The delta that is applied to coordinates before they */ + /* are sent to the emitter, but after the shift. */ + /* */ + /* <Note> */ + /* The point coordinates sent to the emitters are the transformed */ + /* version of the original coordinates (this is important for high */ + /* accuracy during scan-conversion). The transformation is simple: */ + /* */ + /* { */ + /* x' = (x << shift) - delta */ + /* y' = (x << shift) - delta */ + /* } */ + /* */ + /* Set the value of `shift' and `delta' to~0 to get the original */ + /* point coordinates. */ + /* */ + typedef struct FT_Outline_Funcs_ + { + FT_Outline_MoveToFunc move_to; + FT_Outline_LineToFunc line_to; + FT_Outline_ConicToFunc conic_to; + FT_Outline_CubicToFunc cubic_to; + + int shift; + FT_Pos delta; + + } FT_Outline_Funcs; + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* basic_types */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_IMAGE_TAG */ + /* */ + /* <Description> */ + /* This macro converts four-letter tags to an unsigned long type. */ + /* */ + /* <Note> */ + /* Since many 16-bit compilers don't like 32-bit enumerations, you */ + /* should redefine this macro in case of problems to something like */ + /* this: */ + /* */ + /* { */ + /* #define FT_IMAGE_TAG( value, _x1, _x2, _x3, _x4 ) value */ + /* } */ + /* */ + /* to get a simple enumeration without assigning special numbers. */ + /* */ +#ifndef FT_IMAGE_TAG +#define FT_IMAGE_TAG( value, _x1, _x2, _x3, _x4 ) \ + value = ( ( (unsigned long)_x1 << 24 ) | \ + ( (unsigned long)_x2 << 16 ) | \ + ( (unsigned long)_x3 << 8 ) | \ + (unsigned long)_x4 ) +#endif /* FT_IMAGE_TAG */ + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_Glyph_Format */ + /* */ + /* <Description> */ + /* An enumeration type used to describe the format of a given glyph */ + /* image. Note that this version of FreeType only supports two image */ + /* formats, even though future font drivers will be able to register */ + /* their own format. */ + /* */ + /* <Values> */ + /* FT_GLYPH_FORMAT_NONE :: */ + /* The value~0 is reserved. */ + /* */ + /* FT_GLYPH_FORMAT_COMPOSITE :: */ + /* The glyph image is a composite of several other images. This */ + /* format is _only_ used with @FT_LOAD_NO_RECURSE, and is used to */ + /* report compound glyphs (like accented characters). */ + /* */ + /* FT_GLYPH_FORMAT_BITMAP :: */ + /* The glyph image is a bitmap, and can be described as an */ + /* @FT_Bitmap. You generally need to access the `bitmap' field of */ + /* the @FT_GlyphSlotRec structure to read it. */ + /* */ + /* FT_GLYPH_FORMAT_OUTLINE :: */ + /* The glyph image is a vectorial outline made of line segments */ + /* and Bézier arcs; it can be described as an @FT_Outline; you */ + /* generally want to access the `outline' field of the */ + /* @FT_GlyphSlotRec structure to read it. */ + /* */ + /* FT_GLYPH_FORMAT_PLOTTER :: */ + /* The glyph image is a vectorial path with no inside and outside */ + /* contours. Some Type~1 fonts, like those in the Hershey family, */ + /* contain glyphs in this format. These are described as */ + /* @FT_Outline, but FreeType isn't currently capable of rendering */ + /* them correctly. */ + /* */ + typedef enum FT_Glyph_Format_ + { + FT_IMAGE_TAG( FT_GLYPH_FORMAT_NONE, 0, 0, 0, 0 ), + + FT_IMAGE_TAG( FT_GLYPH_FORMAT_COMPOSITE, 'c', 'o', 'm', 'p' ), + FT_IMAGE_TAG( FT_GLYPH_FORMAT_BITMAP, 'b', 'i', 't', 's' ), + FT_IMAGE_TAG( FT_GLYPH_FORMAT_OUTLINE, 'o', 'u', 't', 'l' ), + FT_IMAGE_TAG( FT_GLYPH_FORMAT_PLOTTER, 'p', 'l', 'o', 't' ) + + } FT_Glyph_Format; + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* ft_glyph_format_xxx */ + /* */ + /* <Description> */ + /* A list of deprecated constants. Use the corresponding */ + /* @FT_Glyph_Format values instead. */ + /* */ + /* <Values> */ + /* ft_glyph_format_none :: See @FT_GLYPH_FORMAT_NONE. */ + /* ft_glyph_format_composite :: See @FT_GLYPH_FORMAT_COMPOSITE. */ + /* ft_glyph_format_bitmap :: See @FT_GLYPH_FORMAT_BITMAP. */ + /* ft_glyph_format_outline :: See @FT_GLYPH_FORMAT_OUTLINE. */ + /* ft_glyph_format_plotter :: See @FT_GLYPH_FORMAT_PLOTTER. */ + /* */ +#define ft_glyph_format_none FT_GLYPH_FORMAT_NONE +#define ft_glyph_format_composite FT_GLYPH_FORMAT_COMPOSITE +#define ft_glyph_format_bitmap FT_GLYPH_FORMAT_BITMAP +#define ft_glyph_format_outline FT_GLYPH_FORMAT_OUTLINE +#define ft_glyph_format_plotter FT_GLYPH_FORMAT_PLOTTER + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** R A S T E R D E F I N I T I O N S *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* A raster is a scan converter, in charge of rendering an outline into */ + /* a a bitmap. This section contains the public API for rasters. */ + /* */ + /* Note that in FreeType 2, all rasters are now encapsulated within */ + /* specific modules called `renderers'. See `freetype/ftrender.h' for */ + /* more details on renderers. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* raster */ + /* */ + /* <Title> */ + /* Scanline Converter */ + /* */ + /* <Abstract> */ + /* How vectorial outlines are converted into bitmaps and pixmaps. */ + /* */ + /* <Description> */ + /* This section contains technical definitions. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Raster */ + /* */ + /* <Description> */ + /* A handle (pointer) to a raster object. Each object can be used */ + /* independently to convert an outline into a bitmap or pixmap. */ + /* */ + typedef struct FT_RasterRec_* FT_Raster; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Span */ + /* */ + /* <Description> */ + /* A structure used to model a single span of gray (or black) pixels */ + /* when rendering a monochrome or anti-aliased bitmap. */ + /* */ + /* <Fields> */ + /* x :: The span's horizontal start position. */ + /* */ + /* len :: The span's length in pixels. */ + /* */ + /* coverage :: The span color/coverage, ranging from 0 (background) */ + /* to 255 (foreground). Only used for anti-aliased */ + /* rendering. */ + /* */ + /* <Note> */ + /* This structure is used by the span drawing callback type named */ + /* @FT_SpanFunc which takes the y~coordinate of the span as a */ + /* a parameter. */ + /* */ + /* The coverage value is always between 0 and 255. If you want less */ + /* gray values, the callback function has to reduce them. */ + /* */ + typedef struct FT_Span_ + { + short x; + unsigned short len; + unsigned char coverage; + + } FT_Span; + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_SpanFunc */ + /* */ + /* <Description> */ + /* A function used as a call-back by the anti-aliased renderer in */ + /* order to let client applications draw themselves the gray pixel */ + /* spans on each scan line. */ + /* */ + /* <Input> */ + /* y :: The scanline's y~coordinate. */ + /* */ + /* count :: The number of spans to draw on this scanline. */ + /* */ + /* spans :: A table of `count' spans to draw on the scanline. */ + /* */ + /* user :: User-supplied data that is passed to the callback. */ + /* */ + /* <Note> */ + /* This callback allows client applications to directly render the */ + /* gray spans of the anti-aliased bitmap to any kind of surfaces. */ + /* */ + /* This can be used to write anti-aliased outlines directly to a */ + /* given background bitmap, and even perform translucency. */ + /* */ + /* Note that the `count' field cannot be greater than a fixed value */ + /* defined by the `FT_MAX_GRAY_SPANS' configuration macro in */ + /* `ftoption.h'. By default, this value is set to~32, which means */ + /* that if there are more than 32~spans on a given scanline, the */ + /* callback is called several times with the same `y' parameter in */ + /* order to draw all callbacks. */ + /* */ + /* Otherwise, the callback is only called once per scan-line, and */ + /* only for those scanlines that do have `gray' pixels on them. */ + /* */ + typedef void + (*FT_SpanFunc)( int y, + int count, + const FT_Span* spans, + void* user ); + +#define FT_Raster_Span_Func FT_SpanFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_BitTest_Func */ + /* */ + /* <Description> */ + /* THIS TYPE IS DEPRECATED. DO NOT USE IT. */ + /* */ + /* A function used as a call-back by the monochrome scan-converter */ + /* to test whether a given target pixel is already set to the drawing */ + /* `color'. These tests are crucial to implement drop-out control */ + /* per-se the TrueType spec. */ + /* */ + /* <Input> */ + /* y :: The pixel's y~coordinate. */ + /* */ + /* x :: The pixel's x~coordinate. */ + /* */ + /* user :: User-supplied data that is passed to the callback. */ + /* */ + /* <Return> */ + /* 1~if the pixel is `set', 0~otherwise. */ + /* */ + typedef int + (*FT_Raster_BitTest_Func)( int y, + int x, + void* user ); + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_BitSet_Func */ + /* */ + /* <Description> */ + /* THIS TYPE IS DEPRECATED. DO NOT USE IT. */ + /* */ + /* A function used as a call-back by the monochrome scan-converter */ + /* to set an individual target pixel. This is crucial to implement */ + /* drop-out control according to the TrueType specification. */ + /* */ + /* <Input> */ + /* y :: The pixel's y~coordinate. */ + /* */ + /* x :: The pixel's x~coordinate. */ + /* */ + /* user :: User-supplied data that is passed to the callback. */ + /* */ + /* <Return> */ + /* 1~if the pixel is `set', 0~otherwise. */ + /* */ + typedef void + (*FT_Raster_BitSet_Func)( int y, + int x, + void* user ); + + + /*************************************************************************/ + /* */ + /* <Enum> */ + /* FT_RASTER_FLAG_XXX */ + /* */ + /* <Description> */ + /* A list of bit flag constants as used in the `flags' field of a */ + /* @FT_Raster_Params structure. */ + /* */ + /* <Values> */ + /* FT_RASTER_FLAG_DEFAULT :: This value is 0. */ + /* */ + /* FT_RASTER_FLAG_AA :: This flag is set to indicate that an */ + /* anti-aliased glyph image should be */ + /* generated. Otherwise, it will be */ + /* monochrome (1-bit). */ + /* */ + /* FT_RASTER_FLAG_DIRECT :: This flag is set to indicate direct */ + /* rendering. In this mode, client */ + /* applications must provide their own span */ + /* callback. This lets them directly */ + /* draw or compose over an existing bitmap. */ + /* If this bit is not set, the target */ + /* pixmap's buffer _must_ be zeroed before */ + /* rendering. */ + /* */ + /* Note that for now, direct rendering is */ + /* only possible with anti-aliased glyphs. */ + /* */ + /* FT_RASTER_FLAG_CLIP :: This flag is only used in direct */ + /* rendering mode. If set, the output will */ + /* be clipped to a box specified in the */ + /* `clip_box' field of the */ + /* @FT_Raster_Params structure. */ + /* */ + /* Note that by default, the glyph bitmap */ + /* is clipped to the target pixmap, except */ + /* in direct rendering mode where all spans */ + /* are generated if no clipping box is set. */ + /* */ +#define FT_RASTER_FLAG_DEFAULT 0x0 +#define FT_RASTER_FLAG_AA 0x1 +#define FT_RASTER_FLAG_DIRECT 0x2 +#define FT_RASTER_FLAG_CLIP 0x4 + + /* deprecated */ +#define ft_raster_flag_default FT_RASTER_FLAG_DEFAULT +#define ft_raster_flag_aa FT_RASTER_FLAG_AA +#define ft_raster_flag_direct FT_RASTER_FLAG_DIRECT +#define ft_raster_flag_clip FT_RASTER_FLAG_CLIP + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Raster_Params */ + /* */ + /* <Description> */ + /* A structure to hold the arguments used by a raster's render */ + /* function. */ + /* */ + /* <Fields> */ + /* target :: The target bitmap. */ + /* */ + /* source :: A pointer to the source glyph image (e.g., an */ + /* @FT_Outline). */ + /* */ + /* flags :: The rendering flags. */ + /* */ + /* gray_spans :: The gray span drawing callback. */ + /* */ + /* black_spans :: The black span drawing callback. */ + /* */ + /* bit_test :: The bit test callback. UNIMPLEMENTED! */ + /* */ + /* bit_set :: The bit set callback. UNIMPLEMENTED! */ + /* */ + /* user :: User-supplied data that is passed to each drawing */ + /* callback. */ + /* */ + /* clip_box :: An optional clipping box. It is only used in */ + /* direct rendering mode. Note that coordinates here */ + /* should be expressed in _integer_ pixels (and not in */ + /* 26.6 fixed-point units). */ + /* */ + /* <Note> */ + /* An anti-aliased glyph bitmap is drawn if the @FT_RASTER_FLAG_AA */ + /* bit flag is set in the `flags' field, otherwise a monochrome */ + /* bitmap is generated. */ + /* */ + /* If the @FT_RASTER_FLAG_DIRECT bit flag is set in `flags', the */ + /* raster will call the `gray_spans' callback to draw gray pixel */ + /* spans, in the case of an aa glyph bitmap, it will call */ + /* `black_spans', and `bit_test' and `bit_set' in the case of a */ + /* monochrome bitmap. This allows direct composition over a */ + /* pre-existing bitmap through user-provided callbacks to perform the */ + /* span drawing/composition. */ + /* */ + /* Note that the `bit_test' and `bit_set' callbacks are required when */ + /* rendering a monochrome bitmap, as they are crucial to implement */ + /* correct drop-out control as defined in the TrueType specification. */ + /* */ + typedef struct FT_Raster_Params_ + { + const FT_Bitmap* target; + const void* source; + int flags; + FT_SpanFunc gray_spans; + FT_SpanFunc black_spans; + FT_Raster_BitTest_Func bit_test; /* doesn't work! */ + FT_Raster_BitSet_Func bit_set; /* doesn't work! */ + void* user; + FT_BBox clip_box; + + } FT_Raster_Params; + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_NewFunc */ + /* */ + /* <Description> */ + /* A function used to create a new raster object. */ + /* */ + /* <Input> */ + /* memory :: A handle to the memory allocator. */ + /* */ + /* <Output> */ + /* raster :: A handle to the new raster object. */ + /* */ + /* <Return> */ + /* Error code. 0~means success. */ + /* */ + /* <Note> */ + /* The `memory' parameter is a typeless pointer in order to avoid */ + /* un-wanted dependencies on the rest of the FreeType code. In */ + /* practice, it is an @FT_Memory object, i.e., a handle to the */ + /* standard FreeType memory allocator. However, this field can be */ + /* completely ignored by a given raster implementation. */ + /* */ + typedef int + (*FT_Raster_NewFunc)( void* memory, + FT_Raster* raster ); + +#define FT_Raster_New_Func FT_Raster_NewFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_DoneFunc */ + /* */ + /* <Description> */ + /* A function used to destroy a given raster object. */ + /* */ + /* <Input> */ + /* raster :: A handle to the raster object. */ + /* */ + typedef void + (*FT_Raster_DoneFunc)( FT_Raster raster ); + +#define FT_Raster_Done_Func FT_Raster_DoneFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_ResetFunc */ + /* */ + /* <Description> */ + /* FreeType provides an area of memory called the `render pool', */ + /* available to all registered rasters. This pool can be freely used */ + /* during a given scan-conversion but is shared by all rasters. Its */ + /* content is thus transient. */ + /* */ + /* This function is called each time the render pool changes, or just */ + /* after a new raster object is created. */ + /* */ + /* <Input> */ + /* raster :: A handle to the new raster object. */ + /* */ + /* pool_base :: The address in memory of the render pool. */ + /* */ + /* pool_size :: The size in bytes of the render pool. */ + /* */ + /* <Note> */ + /* Rasters can ignore the render pool and rely on dynamic memory */ + /* allocation if they want to (a handle to the memory allocator is */ + /* passed to the raster constructor). However, this is not */ + /* recommended for efficiency purposes. */ + /* */ + typedef void + (*FT_Raster_ResetFunc)( FT_Raster raster, + unsigned char* pool_base, + unsigned long pool_size ); + +#define FT_Raster_Reset_Func FT_Raster_ResetFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_SetModeFunc */ + /* */ + /* <Description> */ + /* This function is a generic facility to change modes or attributes */ + /* in a given raster. This can be used for debugging purposes, or */ + /* simply to allow implementation-specific `features' in a given */ + /* raster module. */ + /* */ + /* <Input> */ + /* raster :: A handle to the new raster object. */ + /* */ + /* mode :: A 4-byte tag used to name the mode or property. */ + /* */ + /* args :: A pointer to the new mode/property to use. */ + /* */ + typedef int + (*FT_Raster_SetModeFunc)( FT_Raster raster, + unsigned long mode, + void* args ); + +#define FT_Raster_Set_Mode_Func FT_Raster_SetModeFunc + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Raster_RenderFunc */ + /* */ + /* <Description> */ + /* Invoke a given raster to scan-convert a given glyph image into a */ + /* target bitmap. */ + /* */ + /* <Input> */ + /* raster :: A handle to the raster object. */ + /* */ + /* params :: A pointer to an @FT_Raster_Params structure used to */ + /* store the rendering parameters. */ + /* */ + /* <Return> */ + /* Error code. 0~means success. */ + /* */ + /* <Note> */ + /* The exact format of the source image depends on the raster's glyph */ + /* format defined in its @FT_Raster_Funcs structure. It can be an */ + /* @FT_Outline or anything else in order to support a large array of */ + /* glyph formats. */ + /* */ + /* Note also that the render function can fail and return a */ + /* `FT_Err_Unimplemented_Feature' error code if the raster used does */ + /* not support direct composition. */ + /* */ + /* XXX: For now, the standard raster doesn't support direct */ + /* composition but this should change for the final release (see */ + /* the files `demos/src/ftgrays.c' and `demos/src/ftgrays2.c' */ + /* for examples of distinct implementations which support direct */ + /* composition). */ + /* */ + typedef int + (*FT_Raster_RenderFunc)( FT_Raster raster, + const FT_Raster_Params* params ); + +#define FT_Raster_Render_Func FT_Raster_RenderFunc + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Raster_Funcs */ + /* */ + /* <Description> */ + /* A structure used to describe a given raster class to the library. */ + /* */ + /* <Fields> */ + /* glyph_format :: The supported glyph format for this raster. */ + /* */ + /* raster_new :: The raster constructor. */ + /* */ + /* raster_reset :: Used to reset the render pool within the raster. */ + /* */ + /* raster_render :: A function to render a glyph into a given bitmap. */ + /* */ + /* raster_done :: The raster destructor. */ + /* */ + typedef struct FT_Raster_Funcs_ + { + FT_Glyph_Format glyph_format; + FT_Raster_NewFunc raster_new; + FT_Raster_ResetFunc raster_reset; + FT_Raster_SetModeFunc raster_set_mode; + FT_Raster_RenderFunc raster_render; + FT_Raster_DoneFunc raster_done; + + } FT_Raster_Funcs; + + + /* */ + + +FT_END_HEADER + +#endif /* __FTIMAGE_H__ */ + + +/* END */ + + +/* Local Variables: */ +/* coding: utf-8 */ +/* End: */ diff --git a/guilib/freetype2/include/freetype/ftmoderr.h b/guilib/freetype2/include/freetype/ftmoderr.h new file mode 100644 index 0000000000..b0115dd0dd --- /dev/null +++ b/guilib/freetype2/include/freetype/ftmoderr.h @@ -0,0 +1,155 @@ +/***************************************************************************/ +/* */ +/* ftmoderr.h */ +/* */ +/* FreeType module error offsets (specification). */ +/* */ +/* Copyright 2001, 2002, 2003, 2004, 2005 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file is used to define the FreeType module error offsets. */ + /* */ + /* The lower byte gives the error code, the higher byte gives the */ + /* module. The base module has error offset 0. For example, the error */ + /* `FT_Err_Invalid_File_Format' has value 0x003, the error */ + /* `TT_Err_Invalid_File_Format' has value 0x1103, the error */ + /* `T1_Err_Invalid_File_Format' has value 0x1203, etc. */ + /* */ + /* Undefine the macro FT_CONFIG_OPTION_USE_MODULE_ERRORS in ftoption.h */ + /* to make the higher byte always zero (disabling the module error */ + /* mechanism). */ + /* */ + /* It can also be used to create a module error message table easily */ + /* with something like */ + /* */ + /* { */ + /* #undef __FTMODERR_H__ */ + /* #define FT_MODERRDEF( e, v, s ) { FT_Mod_Err_ ## e, s }, */ + /* #define FT_MODERR_START_LIST { */ + /* #define FT_MODERR_END_LIST { 0, 0 } }; */ + /* */ + /* const struct */ + /* { */ + /* int mod_err_offset; */ + /* const char* mod_err_msg */ + /* } ft_mod_errors[] = */ + /* */ + /* #include FT_MODULE_ERRORS_H */ + /* } */ + /* */ + /* To use such a table, all errors must be ANDed with 0xFF00 to remove */ + /* the error code. */ + /* */ + /*************************************************************************/ + + +#ifndef __FTMODERR_H__ +#define __FTMODERR_H__ + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** SETUP MACROS *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + + +#undef FT_NEED_EXTERN_C + +#ifndef FT_MODERRDEF + +#ifdef FT_CONFIG_OPTION_USE_MODULE_ERRORS +#define FT_MODERRDEF( e, v, s ) FT_Mod_Err_ ## e = v, +#else +#define FT_MODERRDEF( e, v, s ) FT_Mod_Err_ ## e = 0, +#endif + +#define FT_MODERR_START_LIST enum { +#define FT_MODERR_END_LIST FT_Mod_Err_Max }; + +#ifdef __cplusplus +#define FT_NEED_EXTERN_C + extern "C" { +#endif + +#endif /* !FT_MODERRDEF */ + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** LIST MODULE ERROR BASES *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + + +#ifdef FT_MODERR_START_LIST + FT_MODERR_START_LIST +#endif + + + FT_MODERRDEF( Base, 0x000, "base module" ) + FT_MODERRDEF( Autofit, 0x100, "autofitter module" ) + FT_MODERRDEF( BDF, 0x200, "BDF module" ) + FT_MODERRDEF( Cache, 0x300, "cache module" ) + FT_MODERRDEF( CFF, 0x400, "CFF module" ) + FT_MODERRDEF( CID, 0x500, "CID module" ) + FT_MODERRDEF( Gzip, 0x600, "Gzip module" ) + FT_MODERRDEF( LZW, 0x700, "LZW module" ) + FT_MODERRDEF( OTvalid, 0x800, "OpenType validation module" ) + FT_MODERRDEF( PCF, 0x900, "PCF module" ) + FT_MODERRDEF( PFR, 0xA00, "PFR module" ) + FT_MODERRDEF( PSaux, 0xB00, "PS auxiliary module" ) + FT_MODERRDEF( PShinter, 0xC00, "PS hinter module" ) + FT_MODERRDEF( PSnames, 0xD00, "PS names module" ) + FT_MODERRDEF( Raster, 0xE00, "raster module" ) + FT_MODERRDEF( SFNT, 0xF00, "SFNT module" ) + FT_MODERRDEF( Smooth, 0x1000, "smooth raster module" ) + FT_MODERRDEF( TrueType, 0x1100, "TrueType module" ) + FT_MODERRDEF( Type1, 0x1200, "Type 1 module" ) + FT_MODERRDEF( Type42, 0x1300, "Type 42 module" ) + FT_MODERRDEF( Winfonts, 0x1400, "Windows FON/FNT module" ) + + +#ifdef FT_MODERR_END_LIST + FT_MODERR_END_LIST +#endif + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** CLEANUP *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + + +#ifdef FT_NEED_EXTERN_C + } +#endif + +#undef FT_MODERR_START_LIST +#undef FT_MODERR_END_LIST +#undef FT_MODERRDEF +#undef FT_NEED_EXTERN_C + + +#endif /* __FTMODERR_H__ */ + + +/* END */ diff --git a/guilib/freetype2/include/freetype/ftoutln.h b/guilib/freetype2/include/freetype/ftoutln.h new file mode 100644 index 0000000000..d7d01e8270 --- /dev/null +++ b/guilib/freetype2/include/freetype/ftoutln.h @@ -0,0 +1,538 @@ +/***************************************************************************/ +/* */ +/* ftoutln.h */ +/* */ +/* Support for the FT_Outline type used to store glyph shapes of */ +/* most scalable font formats (specification). */ +/* */ +/* Copyright 1996-2001, 2002, 2003, 2005, 2006, 2007, 2008, 2009 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTOUTLN_H__ +#define __FTOUTLN_H__ + + +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef FREETYPE_H +#error "freetype.h of FreeType 1 has been loaded!" +#error "Please fix the directory search order for header files" +#error "so that freetype.h of FreeType 2 is found first." +#endif + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* outline_processing */ + /* */ + /* <Title> */ + /* Outline Processing */ + /* */ + /* <Abstract> */ + /* Functions to create, transform, and render vectorial glyph images. */ + /* */ + /* <Description> */ + /* This section contains routines used to create and destroy scalable */ + /* glyph images known as `outlines'. These can also be measured, */ + /* transformed, and converted into bitmaps and pixmaps. */ + /* */ + /* <Order> */ + /* FT_Outline */ + /* FT_OUTLINE_FLAGS */ + /* FT_Outline_New */ + /* FT_Outline_Done */ + /* FT_Outline_Copy */ + /* FT_Outline_Translate */ + /* FT_Outline_Transform */ + /* FT_Outline_Embolden */ + /* FT_Outline_Reverse */ + /* FT_Outline_Check */ + /* */ + /* FT_Outline_Get_CBox */ + /* FT_Outline_Get_BBox */ + /* */ + /* FT_Outline_Get_Bitmap */ + /* FT_Outline_Render */ + /* */ + /* FT_Outline_Decompose */ + /* FT_Outline_Funcs */ + /* FT_Outline_MoveTo_Func */ + /* FT_Outline_LineTo_Func */ + /* FT_Outline_ConicTo_Func */ + /* FT_Outline_CubicTo_Func */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Decompose */ + /* */ + /* <Description> */ + /* Walk over an outline's structure to decompose it into individual */ + /* segments and Bézier arcs. This function is also able to emit */ + /* `move to' and `close to' operations to indicate the start and end */ + /* of new contours in the outline. */ + /* */ + /* <Input> */ + /* outline :: A pointer to the source target. */ + /* */ + /* func_interface :: A table of `emitters', i.e., function pointers */ + /* called during decomposition to indicate path */ + /* operations. */ + /* */ + /* <InOut> */ + /* user :: A typeless pointer which is passed to each */ + /* emitter during the decomposition. It can be */ + /* used to store the state during the */ + /* decomposition. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Decompose( FT_Outline* outline, + const FT_Outline_Funcs* func_interface, + void* user ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_New */ + /* */ + /* <Description> */ + /* Create a new outline of a given size. */ + /* */ + /* <Input> */ + /* library :: A handle to the library object from where the */ + /* outline is allocated. Note however that the new */ + /* outline will *not* necessarily be *freed*, when */ + /* destroying the library, by @FT_Done_FreeType. */ + /* */ + /* numPoints :: The maximal number of points within the outline. */ + /* */ + /* numContours :: The maximal number of contours within the outline. */ + /* */ + /* <Output> */ + /* anoutline :: A handle to the new outline. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The reason why this function takes a `library' parameter is simply */ + /* to use the library's memory allocator. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_New( FT_Library library, + FT_UInt numPoints, + FT_Int numContours, + FT_Outline *anoutline ); + + + FT_EXPORT( FT_Error ) + FT_Outline_New_Internal( FT_Memory memory, + FT_UInt numPoints, + FT_Int numContours, + FT_Outline *anoutline ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Done */ + /* */ + /* <Description> */ + /* Destroy an outline created with @FT_Outline_New. */ + /* */ + /* <Input> */ + /* library :: A handle of the library object used to allocate the */ + /* outline. */ + /* */ + /* outline :: A pointer to the outline object to be discarded. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* If the outline's `owner' field is not set, only the outline */ + /* descriptor will be released. */ + /* */ + /* The reason why this function takes an `library' parameter is */ + /* simply to use ft_mem_free(). */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Done( FT_Library library, + FT_Outline* outline ); + + + FT_EXPORT( FT_Error ) + FT_Outline_Done_Internal( FT_Memory memory, + FT_Outline* outline ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Check */ + /* */ + /* <Description> */ + /* Check the contents of an outline descriptor. */ + /* */ + /* <Input> */ + /* outline :: A handle to a source outline. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Check( FT_Outline* outline ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Get_CBox */ + /* */ + /* <Description> */ + /* Return an outline's `control box'. The control box encloses all */ + /* the outline's points, including Bézier control points. Though it */ + /* coincides with the exact bounding box for most glyphs, it can be */ + /* slightly larger in some situations (like when rotating an outline */ + /* which contains Bézier outside arcs). */ + /* */ + /* Computing the control box is very fast, while getting the bounding */ + /* box can take much more time as it needs to walk over all segments */ + /* and arcs in the outline. To get the latter, you can use the */ + /* `ftbbox' component which is dedicated to this single task. */ + /* */ + /* <Input> */ + /* outline :: A pointer to the source outline descriptor. */ + /* */ + /* <Output> */ + /* acbox :: The outline's control box. */ + /* */ + FT_EXPORT( void ) + FT_Outline_Get_CBox( const FT_Outline* outline, + FT_BBox *acbox ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Translate */ + /* */ + /* <Description> */ + /* Apply a simple translation to the points of an outline. */ + /* */ + /* <InOut> */ + /* outline :: A pointer to the target outline descriptor. */ + /* */ + /* <Input> */ + /* xOffset :: The horizontal offset. */ + /* */ + /* yOffset :: The vertical offset. */ + /* */ + FT_EXPORT( void ) + FT_Outline_Translate( const FT_Outline* outline, + FT_Pos xOffset, + FT_Pos yOffset ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Copy */ + /* */ + /* <Description> */ + /* Copy an outline into another one. Both objects must have the */ + /* same sizes (number of points & number of contours) when this */ + /* function is called. */ + /* */ + /* <Input> */ + /* source :: A handle to the source outline. */ + /* */ + /* <Output> */ + /* target :: A handle to the target outline. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Copy( const FT_Outline* source, + FT_Outline *target ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Transform */ + /* */ + /* <Description> */ + /* Apply a simple 2x2 matrix to all of an outline's points. Useful */ + /* for applying rotations, slanting, flipping, etc. */ + /* */ + /* <InOut> */ + /* outline :: A pointer to the target outline descriptor. */ + /* */ + /* <Input> */ + /* matrix :: A pointer to the transformation matrix. */ + /* */ + /* <Note> */ + /* You can use @FT_Outline_Translate if you need to translate the */ + /* outline's points. */ + /* */ + FT_EXPORT( void ) + FT_Outline_Transform( const FT_Outline* outline, + const FT_Matrix* matrix ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Embolden */ + /* */ + /* <Description> */ + /* Embolden an outline. The new outline will be at most 4~times */ + /* `strength' pixels wider and higher. You may think of the left and */ + /* bottom borders as unchanged. */ + /* */ + /* Negative `strength' values to reduce the outline thickness are */ + /* possible also. */ + /* */ + /* <InOut> */ + /* outline :: A handle to the target outline. */ + /* */ + /* <Input> */ + /* strength :: How strong the glyph is emboldened. Expressed in */ + /* 26.6 pixel format. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* The used algorithm to increase or decrease the thickness of the */ + /* glyph doesn't change the number of points; this means that certain */ + /* situations like acute angles or intersections are sometimes */ + /* handled incorrectly. */ + /* */ + /* If you need `better' metrics values you should call */ + /* @FT_Outline_Get_CBox ot @FT_Outline_Get_BBox. */ + /* */ + /* Example call: */ + /* */ + /* { */ + /* FT_Load_Glyph( face, index, FT_LOAD_DEFAULT ); */ + /* if ( face->slot->format == FT_GLYPH_FORMAT_OUTLINE ) */ + /* FT_Outline_Embolden( &face->slot->outline, strength ); */ + /* } */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Embolden( FT_Outline* outline, + FT_Pos strength ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Reverse */ + /* */ + /* <Description> */ + /* Reverse the drawing direction of an outline. This is used to */ + /* ensure consistent fill conventions for mirrored glyphs. */ + /* */ + /* <InOut> */ + /* outline :: A pointer to the target outline descriptor. */ + /* */ + /* <Note> */ + /* This function toggles the bit flag @FT_OUTLINE_REVERSE_FILL in */ + /* the outline's `flags' field. */ + /* */ + /* It shouldn't be used by a normal client application, unless it */ + /* knows what it is doing. */ + /* */ + FT_EXPORT( void ) + FT_Outline_Reverse( FT_Outline* outline ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Get_Bitmap */ + /* */ + /* <Description> */ + /* Render an outline within a bitmap. The outline's image is simply */ + /* OR-ed to the target bitmap. */ + /* */ + /* <Input> */ + /* library :: A handle to a FreeType library object. */ + /* */ + /* outline :: A pointer to the source outline descriptor. */ + /* */ + /* <InOut> */ + /* abitmap :: A pointer to the target bitmap descriptor. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* This function does NOT CREATE the bitmap, it only renders an */ + /* outline image within the one you pass to it! Consequently, the */ + /* various fields in `abitmap' should be set accordingly. */ + /* */ + /* It will use the raster corresponding to the default glyph format. */ + /* */ + /* The value of the `num_grays' field in `abitmap' is ignored. If */ + /* you select the gray-level rasterizer, and you want less than 256 */ + /* gray levels, you have to use @FT_Outline_Render directly. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Get_Bitmap( FT_Library library, + FT_Outline* outline, + const FT_Bitmap *abitmap ); + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* FT_Outline_Render */ + /* */ + /* <Description> */ + /* Render an outline within a bitmap using the current scan-convert. */ + /* This function uses an @FT_Raster_Params structure as an argument, */ + /* allowing advanced features like direct composition, translucency, */ + /* etc. */ + /* */ + /* <Input> */ + /* library :: A handle to a FreeType library object. */ + /* */ + /* outline :: A pointer to the source outline descriptor. */ + /* */ + /* <InOut> */ + /* params :: A pointer to an @FT_Raster_Params structure used to */ + /* describe the rendering operation. */ + /* */ + /* <Return> */ + /* FreeType error code. 0~means success. */ + /* */ + /* <Note> */ + /* You should know what you are doing and how @FT_Raster_Params works */ + /* to use this function. */ + /* */ + /* The field `params.source' will be set to `outline' before the scan */ + /* converter is called, which means that the value you give to it is */ + /* actually ignored. */ + /* */ + /* The gray-level rasterizer always uses 256 gray levels. If you */ + /* want less gray levels, you have to provide your own span callback. */ + /* See the @FT_RASTER_FLAG_DIRECT value of the `flags' field in the */ + /* @FT_Raster_Params structure for more details. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Outline_Render( FT_Library library, + FT_Outline* outline, + FT_Raster_Params* params ); + + + /************************************************************************** + * + * @enum: + * FT_Orientation + * + * @description: + * A list of values used to describe an outline's contour orientation. + * + * The TrueType and PostScript specifications use different conventions + * to determine whether outline contours should be filled or unfilled. + * + * @values: + * FT_ORIENTATION_TRUETYPE :: + * According to the TrueType specification, clockwise contours must + * be filled, and counter-clockwise ones must be unfilled. + * + * FT_ORIENTATION_POSTSCRIPT :: + * According to the PostScript specification, counter-clockwise contours + * must be filled, and clockwise ones must be unfilled. + * + * FT_ORIENTATION_FILL_RIGHT :: + * This is identical to @FT_ORIENTATION_TRUETYPE, but is used to + * remember that in TrueType, everything that is to the right of + * the drawing direction of a contour must be filled. + * + * FT_ORIENTATION_FILL_LEFT :: + * This is identical to @FT_ORIENTATION_POSTSCRIPT, but is used to + * remember that in PostScript, everything that is to the left of + * the drawing direction of a contour must be filled. + * + * FT_ORIENTATION_NONE :: + * The orientation cannot be determined. That is, different parts of + * the glyph have different orientation. + * + */ + typedef enum FT_Orientation_ + { + FT_ORIENTATION_TRUETYPE = 0, + FT_ORIENTATION_POSTSCRIPT = 1, + FT_ORIENTATION_FILL_RIGHT = FT_ORIENTATION_TRUETYPE, + FT_ORIENTATION_FILL_LEFT = FT_ORIENTATION_POSTSCRIPT, + FT_ORIENTATION_NONE + + } FT_Orientation; + + + /************************************************************************** + * + * @function: + * FT_Outline_Get_Orientation + * + * @description: + * This function analyzes a glyph outline and tries to compute its + * fill orientation (see @FT_Orientation). This is done by computing + * the direction of each global horizontal and/or vertical extrema + * within the outline. + * + * Note that this will return @FT_ORIENTATION_TRUETYPE for empty + * outlines. + * + * @input: + * outline :: + * A handle to the source outline. + * + * @return: + * The orientation. + * + */ + FT_EXPORT( FT_Orientation ) + FT_Outline_Get_Orientation( FT_Outline* outline ); + + + /* */ + + +FT_END_HEADER + +#endif /* __FTOUTLN_H__ */ + + +/* END */ + + +/* Local Variables: */ +/* coding: utf-8 */ +/* End: */ diff --git a/guilib/freetype2/include/freetype/ftsystem.h b/guilib/freetype2/include/freetype/ftsystem.h new file mode 100644 index 0000000000..a95b2c76b6 --- /dev/null +++ b/guilib/freetype2/include/freetype/ftsystem.h @@ -0,0 +1,346 @@ +/***************************************************************************/ +/* */ +/* ftsystem.h */ +/* */ +/* FreeType low-level system interface definition (specification). */ +/* */ +/* Copyright 1996-2001, 2002, 2005 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTSYSTEM_H__ +#define __FTSYSTEM_H__ + + +#include <ft2build.h> + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* system_interface */ + /* */ + /* <Title> */ + /* System Interface */ + /* */ + /* <Abstract> */ + /* How FreeType manages memory and i/o. */ + /* */ + /* <Description> */ + /* This section contains various definitions related to memory */ + /* management and i/o access. You need to understand this */ + /* information if you want to use a custom memory manager or you own */ + /* i/o streams. */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* M E M O R Y M A N A G E M E N T */ + /* */ + /*************************************************************************/ + + + /************************************************************************* + * + * @type: + * FT_Memory + * + * @description: + * A handle to a given memory manager object, defined with an + * @FT_MemoryRec structure. + * + */ + typedef struct FT_MemoryRec_* FT_Memory; + + + /************************************************************************* + * + * @functype: + * FT_Alloc_Func + * + * @description: + * A function used to allocate `size' bytes from `memory'. + * + * @input: + * memory :: + * A handle to the source memory manager. + * + * size :: + * The size in bytes to allocate. + * + * @return: + * Address of new memory block. 0~in case of failure. + * + */ + typedef void* + (*FT_Alloc_Func)( FT_Memory memory, + long size ); + + + /************************************************************************* + * + * @functype: + * FT_Free_Func + * + * @description: + * A function used to release a given block of memory. + * + * @input: + * memory :: + * A handle to the source memory manager. + * + * block :: + * The address of the target memory block. + * + */ + typedef void + (*FT_Free_Func)( FT_Memory memory, + void* block ); + + + /************************************************************************* + * + * @functype: + * FT_Realloc_Func + * + * @description: + * A function used to re-allocate a given block of memory. + * + * @input: + * memory :: + * A handle to the source memory manager. + * + * cur_size :: + * The block's current size in bytes. + * + * new_size :: + * The block's requested new size. + * + * block :: + * The block's current address. + * + * @return: + * New block address. 0~in case of memory shortage. + * + * @note: + * In case of error, the old block must still be available. + * + */ + typedef void* + (*FT_Realloc_Func)( FT_Memory memory, + long cur_size, + long new_size, + void* block ); + + + /************************************************************************* + * + * @struct: + * FT_MemoryRec + * + * @description: + * A structure used to describe a given memory manager to FreeType~2. + * + * @fields: + * user :: + * A generic typeless pointer for user data. + * + * alloc :: + * A pointer type to an allocation function. + * + * free :: + * A pointer type to an memory freeing function. + * + * realloc :: + * A pointer type to a reallocation function. + * + */ + struct FT_MemoryRec_ + { + void* user; + FT_Alloc_Func alloc; + FT_Free_Func free; + FT_Realloc_Func realloc; + }; + + + /*************************************************************************/ + /* */ + /* I / O M A N A G E M E N T */ + /* */ + /*************************************************************************/ + + + /************************************************************************* + * + * @type: + * FT_Stream + * + * @description: + * A handle to an input stream. + * + */ + typedef struct FT_StreamRec_* FT_Stream; + + + /************************************************************************* + * + * @struct: + * FT_StreamDesc + * + * @description: + * A union type used to store either a long or a pointer. This is used + * to store a file descriptor or a `FILE*' in an input stream. + * + */ + typedef union FT_StreamDesc_ + { + long value; + void* pointer; + + } FT_StreamDesc; + + + /************************************************************************* + * + * @functype: + * FT_Stream_IoFunc + * + * @description: + * A function used to seek and read data from a given input stream. + * + * @input: + * stream :: + * A handle to the source stream. + * + * offset :: + * The offset of read in stream (always from start). + * + * buffer :: + * The address of the read buffer. + * + * count :: + * The number of bytes to read from the stream. + * + * @return: + * The number of bytes effectively read by the stream. + * + * @note: + * This function might be called to perform a seek or skip operation + * with a `count' of~0. + * + */ + typedef unsigned long + (*FT_Stream_IoFunc)( FT_Stream stream, + unsigned long offset, + unsigned char* buffer, + unsigned long count ); + + + /************************************************************************* + * + * @functype: + * FT_Stream_CloseFunc + * + * @description: + * A function used to close a given input stream. + * + * @input: + * stream :: + * A handle to the target stream. + * + */ + typedef void + (*FT_Stream_CloseFunc)( FT_Stream stream ); + + + /************************************************************************* + * + * @struct: + * FT_StreamRec + * + * @description: + * A structure used to describe an input stream. + * + * @input: + * base :: + * For memory-based streams, this is the address of the first stream + * byte in memory. This field should always be set to NULL for + * disk-based streams. + * + * size :: + * The stream size in bytes. + * + * pos :: + * The current position within the stream. + * + * descriptor :: + * This field is a union that can hold an integer or a pointer. It is + * used by stream implementations to store file descriptors or `FILE*' + * pointers. + * + * pathname :: + * This field is completely ignored by FreeType. However, it is often + * useful during debugging to use it to store the stream's filename + * (where available). + * + * read :: + * The stream's input function. + * + * close :: + * The stream;s close function. + * + * memory :: + * The memory manager to use to preload frames. This is set + * internally by FreeType and shouldn't be touched by stream + * implementations. + * + * cursor :: + * This field is set and used internally by FreeType when parsing + * frames. + * + * limit :: + * This field is set and used internally by FreeType when parsing + * frames. + * + */ + typedef struct FT_StreamRec_ + { + unsigned char* base; + unsigned long size; + unsigned long pos; + + FT_StreamDesc descriptor; + FT_StreamDesc pathname; + FT_Stream_IoFunc read; + FT_Stream_CloseFunc close; + + FT_Memory memory; + unsigned char* cursor; + unsigned char* limit; + + } FT_StreamRec; + + + /* */ + + +FT_END_HEADER + +#endif /* __FTSYSTEM_H__ */ + + +/* END */ diff --git a/guilib/freetype2/include/freetype/fttypes.h b/guilib/freetype2/include/freetype/fttypes.h new file mode 100644 index 0000000000..54f08e3e5a --- /dev/null +++ b/guilib/freetype2/include/freetype/fttypes.h @@ -0,0 +1,587 @@ +/***************************************************************************/ +/* */ +/* fttypes.h */ +/* */ +/* FreeType simple types definitions (specification only). */ +/* */ +/* Copyright 1996-2001, 2002, 2004, 2006, 2007, 2008 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#ifndef __FTTYPES_H__ +#define __FTTYPES_H__ + + +#include <ft2build.h> +#include FT_CONFIG_CONFIG_H +#include FT_SYSTEM_H +#include FT_IMAGE_H + +#include <stddef.h> + + +FT_BEGIN_HEADER + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* basic_types */ + /* */ + /* <Title> */ + /* Basic Data Types */ + /* */ + /* <Abstract> */ + /* The basic data types defined by the library. */ + /* */ + /* <Description> */ + /* This section contains the basic data types defined by FreeType~2, */ + /* ranging from simple scalar types to bitmap descriptors. More */ + /* font-specific structures are defined in a different section. */ + /* */ + /* <Order> */ + /* FT_Byte */ + /* FT_Bytes */ + /* FT_Char */ + /* FT_Int */ + /* FT_UInt */ + /* FT_Int16 */ + /* FT_UInt16 */ + /* FT_Int32 */ + /* FT_UInt32 */ + /* FT_Short */ + /* FT_UShort */ + /* FT_Long */ + /* FT_ULong */ + /* FT_Bool */ + /* FT_Offset */ + /* FT_PtrDist */ + /* FT_String */ + /* FT_Tag */ + /* FT_Error */ + /* FT_Fixed */ + /* FT_Pointer */ + /* FT_Pos */ + /* FT_Vector */ + /* FT_BBox */ + /* FT_Matrix */ + /* FT_FWord */ + /* FT_UFWord */ + /* FT_F2Dot14 */ + /* FT_UnitVector */ + /* FT_F26Dot6 */ + /* */ + /* */ + /* FT_Generic */ + /* FT_Generic_Finalizer */ + /* */ + /* FT_Bitmap */ + /* FT_Pixel_Mode */ + /* FT_Palette_Mode */ + /* FT_Glyph_Format */ + /* FT_IMAGE_TAG */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Bool */ + /* */ + /* <Description> */ + /* A typedef of unsigned char, used for simple booleans. As usual, */ + /* values 1 and~0 represent true and false, respectively. */ + /* */ + typedef unsigned char FT_Bool; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_FWord */ + /* */ + /* <Description> */ + /* A signed 16-bit integer used to store a distance in original font */ + /* units. */ + /* */ + typedef signed short FT_FWord; /* distance in FUnits */ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_UFWord */ + /* */ + /* <Description> */ + /* An unsigned 16-bit integer used to store a distance in original */ + /* font units. */ + /* */ + typedef unsigned short FT_UFWord; /* unsigned distance */ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Char */ + /* */ + /* <Description> */ + /* A simple typedef for the _signed_ char type. */ + /* */ + typedef signed char FT_Char; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Byte */ + /* */ + /* <Description> */ + /* A simple typedef for the _unsigned_ char type. */ + /* */ + typedef unsigned char FT_Byte; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Bytes */ + /* */ + /* <Description> */ + /* A typedef for constant memory areas. */ + /* */ + typedef const FT_Byte* FT_Bytes; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Tag */ + /* */ + /* <Description> */ + /* A typedef for 32-bit tags (as used in the SFNT format). */ + /* */ + typedef FT_UInt32 FT_Tag; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_String */ + /* */ + /* <Description> */ + /* A simple typedef for the char type, usually used for strings. */ + /* */ + typedef char FT_String; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Short */ + /* */ + /* <Description> */ + /* A typedef for signed short. */ + /* */ + typedef signed short FT_Short; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_UShort */ + /* */ + /* <Description> */ + /* A typedef for unsigned short. */ + /* */ + typedef unsigned short FT_UShort; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Int */ + /* */ + /* <Description> */ + /* A typedef for the int type. */ + /* */ + typedef signed int FT_Int; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_UInt */ + /* */ + /* <Description> */ + /* A typedef for the unsigned int type. */ + /* */ + typedef unsigned int FT_UInt; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Long */ + /* */ + /* <Description> */ + /* A typedef for signed long. */ + /* */ + typedef signed long FT_Long; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_ULong */ + /* */ + /* <Description> */ + /* A typedef for unsigned long. */ + /* */ + typedef unsigned long FT_ULong; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_F2Dot14 */ + /* */ + /* <Description> */ + /* A signed 2.14 fixed float type used for unit vectors. */ + /* */ + typedef signed short FT_F2Dot14; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_F26Dot6 */ + /* */ + /* <Description> */ + /* A signed 26.6 fixed float type used for vectorial pixel */ + /* coordinates. */ + /* */ + typedef signed long FT_F26Dot6; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Fixed */ + /* */ + /* <Description> */ + /* This type is used to store 16.16 fixed float values, like scaling */ + /* values or matrix coefficients. */ + /* */ + typedef signed long FT_Fixed; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Error */ + /* */ + /* <Description> */ + /* The FreeType error code type. A value of~0 is always interpreted */ + /* as a successful operation. */ + /* */ + typedef int FT_Error; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Pointer */ + /* */ + /* <Description> */ + /* A simple typedef for a typeless pointer. */ + /* */ + typedef void* FT_Pointer; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_Offset */ + /* */ + /* <Description> */ + /* This is equivalent to the ANSI~C `size_t' type, i.e., the largest */ + /* _unsigned_ integer type used to express a file size or position, */ + /* or a memory block size. */ + /* */ + typedef size_t FT_Offset; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_PtrDist */ + /* */ + /* <Description> */ + /* This is equivalent to the ANSI~C `ptrdiff_t' type, i.e., the */ + /* largest _signed_ integer type used to express the distance */ + /* between two pointers. */ + /* */ + typedef ft_ptrdiff_t FT_PtrDist; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_UnitVector */ + /* */ + /* <Description> */ + /* A simple structure used to store a 2D vector unit vector. Uses */ + /* FT_F2Dot14 types. */ + /* */ + /* <Fields> */ + /* x :: Horizontal coordinate. */ + /* */ + /* y :: Vertical coordinate. */ + /* */ + typedef struct FT_UnitVector_ + { + FT_F2Dot14 x; + FT_F2Dot14 y; + + } FT_UnitVector; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Matrix */ + /* */ + /* <Description> */ + /* A simple structure used to store a 2x2 matrix. Coefficients are */ + /* in 16.16 fixed float format. The computation performed is: */ + /* */ + /* { */ + /* x' = x*xx + y*xy */ + /* y' = x*yx + y*yy */ + /* } */ + /* */ + /* <Fields> */ + /* xx :: Matrix coefficient. */ + /* */ + /* xy :: Matrix coefficient. */ + /* */ + /* yx :: Matrix coefficient. */ + /* */ + /* yy :: Matrix coefficient. */ + /* */ + typedef struct FT_Matrix_ + { + FT_Fixed xx, xy; + FT_Fixed yx, yy; + + } FT_Matrix; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Data */ + /* */ + /* <Description> */ + /* Read-only binary data represented as a pointer and a length. */ + /* */ + /* <Fields> */ + /* pointer :: The data. */ + /* */ + /* length :: The length of the data in bytes. */ + /* */ + typedef struct FT_Data_ + { + const FT_Byte* pointer; + FT_Int length; + + } FT_Data; + + + /*************************************************************************/ + /* */ + /* <FuncType> */ + /* FT_Generic_Finalizer */ + /* */ + /* <Description> */ + /* Describe a function used to destroy the `client' data of any */ + /* FreeType object. See the description of the @FT_Generic type for */ + /* details of usage. */ + /* */ + /* <Input> */ + /* The address of the FreeType object which is under finalization. */ + /* Its client data is accessed through its `generic' field. */ + /* */ + typedef void (*FT_Generic_Finalizer)(void* object); + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_Generic */ + /* */ + /* <Description> */ + /* Client applications often need to associate their own data to a */ + /* variety of FreeType core objects. For example, a text layout API */ + /* might want to associate a glyph cache to a given size object. */ + /* */ + /* Most FreeType object contains a `generic' field, of type */ + /* FT_Generic, which usage is left to client applications and font */ + /* servers. */ + /* */ + /* It can be used to store a pointer to client-specific data, as well */ + /* as the address of a `finalizer' function, which will be called by */ + /* FreeType when the object is destroyed (for example, the previous */ + /* client example would put the address of the glyph cache destructor */ + /* in the `finalizer' field). */ + /* */ + /* <Fields> */ + /* data :: A typeless pointer to any client-specified data. This */ + /* field is completely ignored by the FreeType library. */ + /* */ + /* finalizer :: A pointer to a `generic finalizer' function, which */ + /* will be called when the object is destroyed. If this */ + /* field is set to NULL, no code will be called. */ + /* */ + typedef struct FT_Generic_ + { + void* data; + FT_Generic_Finalizer finalizer; + + } FT_Generic; + + + /*************************************************************************/ + /* */ + /* <Macro> */ + /* FT_MAKE_TAG */ + /* */ + /* <Description> */ + /* This macro converts four-letter tags which are used to label */ + /* TrueType tables into an unsigned long to be used within FreeType. */ + /* */ + /* <Note> */ + /* The produced values *must* be 32-bit integers. Don't redefine */ + /* this macro. */ + /* */ +#define FT_MAKE_TAG( _x1, _x2, _x3, _x4 ) \ + ( ( (FT_ULong)_x1 << 24 ) | \ + ( (FT_ULong)_x2 << 16 ) | \ + ( (FT_ULong)_x3 << 8 ) | \ + (FT_ULong)_x4 ) + + + /*************************************************************************/ + /*************************************************************************/ + /* */ + /* L I S T M A N A G E M E N T */ + /* */ + /*************************************************************************/ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Section> */ + /* list_processing */ + /* */ + /*************************************************************************/ + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_ListNode */ + /* */ + /* <Description> */ + /* Many elements and objects in FreeType are listed through an */ + /* @FT_List record (see @FT_ListRec). As its name suggests, an */ + /* FT_ListNode is a handle to a single list element. */ + /* */ + typedef struct FT_ListNodeRec_* FT_ListNode; + + + /*************************************************************************/ + /* */ + /* <Type> */ + /* FT_List */ + /* */ + /* <Description> */ + /* A handle to a list record (see @FT_ListRec). */ + /* */ + typedef struct FT_ListRec_* FT_List; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_ListNodeRec */ + /* */ + /* <Description> */ + /* A structure used to hold a single list element. */ + /* */ + /* <Fields> */ + /* prev :: The previous element in the list. NULL if first. */ + /* */ + /* next :: The next element in the list. NULL if last. */ + /* */ + /* data :: A typeless pointer to the listed object. */ + /* */ + typedef struct FT_ListNodeRec_ + { + FT_ListNode prev; + FT_ListNode next; + void* data; + + } FT_ListNodeRec; + + + /*************************************************************************/ + /* */ + /* <Struct> */ + /* FT_ListRec */ + /* */ + /* <Description> */ + /* A structure used to hold a simple doubly-linked list. These are */ + /* used in many parts of FreeType. */ + /* */ + /* <Fields> */ + /* head :: The head (first element) of doubly-linked list. */ + /* */ + /* tail :: The tail (last element) of doubly-linked list. */ + /* */ + typedef struct FT_ListRec_ + { + FT_ListNode head; + FT_ListNode tail; + + } FT_ListRec; + + + /* */ + +#define FT_IS_EMPTY( list ) ( (list).head == 0 ) + + /* return base error code (without module-specific prefix) */ +#define FT_ERROR_BASE( x ) ( (x) & 0xFF ) + + /* return module error code */ +#define FT_ERROR_MODULE( x ) ( (x) & 0xFF00U ) + +#define FT_BOOL( x ) ( (FT_Bool)( x ) ) + +FT_END_HEADER + +#endif /* __FTTYPES_H__ */ + + +/* END */ diff --git a/guilib/freetype2/include/ft2build.h b/guilib/freetype2/include/ft2build.h new file mode 100644 index 0000000000..923d887df6 --- /dev/null +++ b/guilib/freetype2/include/ft2build.h @@ -0,0 +1,39 @@ +/***************************************************************************/ +/* */ +/* ft2build.h */ +/* */ +/* FreeType 2 build and setup macros. */ +/* (Generic version) */ +/* */ +/* Copyright 1996-2001, 2006 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + + /*************************************************************************/ + /* */ + /* This file corresponds to the default `ft2build.h' file for */ + /* FreeType 2. It uses the `freetype' include root. */ + /* */ + /* Note that specific platforms might use a different configuration. */ + /* See builds/unix/ft2unix.h for an example. */ + /* */ + /*************************************************************************/ + + +#ifndef __FT2_BUILD_GENERIC_H__ +#define __FT2_BUILD_GENERIC_H__ + +#include <freetype/config/ftheader.h> + +#endif /* __FT2_BUILD_GENERIC_H__ */ + + +/* END */ diff --git a/guilib/gui3d.h b/guilib/gui3d.h new file mode 100644 index 0000000000..9ce1d988fb --- /dev/null +++ b/guilib/gui3d.h @@ -0,0 +1,107 @@ +/*! +\file gui3d.h +\brief +*/ + +#ifndef GUILIB_GUI3D_H +#define GUILIB_GUI3D_H +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" + +#define GAMMA_RAMP_FLAG D3DSGR_CALIBRATE + +#define D3DPRESENTFLAG_INTERLACED 1 +#define D3DPRESENTFLAG_WIDESCREEN 2 +#define D3DPRESENTFLAG_PROGRESSIVE 4 + +#define D3DFMT_LIN_A8R8G8B8 D3DFMT_A8R8G8B8 +#define D3DFMT_LIN_X8R8G8B8 D3DFMT_X8R8G8B8 +#define D3DFMT_LIN_L8 D3DFMT_L8 +#define D3DFMT_LIN_D16 D3DFMT_D16 +#define D3DFMT_LIN_A8 D3DFMT_A8 + +#define D3DPIXELSHADERDEF DWORD + +struct D3DTexture +{ + DWORD Common; + DWORD Data; + DWORD Lock; + + DWORD Format; // Format information about the texture. + DWORD Size; // Size of a non power-of-2 texture, must be zero otherwise +}; + +#define D3DCOMMON_TYPE_MASK 0x0070000 +#define D3DCOMMON_TYPE_TEXTURE 0x0040000 + +struct D3DPalette +{ + DWORD Common; + DWORD Data; + DWORD Lock; +}; + +typedef D3DPalette* LPDIRECT3DPALETTE8; + +#if defined(HAS_GL) || defined(HAS_GLES) + +namespace XBMC +{ + typedef void* DevicePtr; + typedef GLuint SurfacePtr; + typedef GLuint TexturePtr; + typedef void* PalettePtr; // elis change it + typedef GLint PixelFormat; // elis change it +} + +#if defined(_LINUX) && !defined(GL_GLEXT_PROTOTYPES) +#define GL_GLEXT_PROTOTYPES +#endif + +#endif // HAS_GL + +#ifdef HAS_DX + +namespace XBMC +{ + typedef LPDIRECT3DDEVICE9 DevicePtr; + typedef LPDIRECT3DTEXTURE9 TexturePtr; + typedef LPDIRECT3DSURFACE9 SurfacePtr; + typedef LPDIRECT3DPALETTE8 PalettePtr; +}; + +#define DELETE_TEXTURE(texture) texture->Release() + +typedef uint32_t Uint32; + +#endif // HAS_DX + +#ifdef HAS_GLES + +#define GLchar char + +#endif +#endif // GUILIB_GUI3D_H diff --git a/guilib/guilib_win32.vcproj b/guilib/guilib_win32.vcproj new file mode 100644 index 0000000000..794cf9d115 --- /dev/null +++ b/guilib/guilib_win32.vcproj @@ -0,0 +1,772 @@ +<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="guilib"
+ ProjectGUID="{510441AC-B9E1-4B31-9C0C-EB3AD39D90C4}"
+ RootNamespace="guilib"
+ Keyword="Win32Proj">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="Debug (Win32)"
+ IntermediateDirectory="Debug (Win32)"
+ ConfigurationType="4"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/EHa"
+ Optimization="0"
+ AdditionalIncludeDirectories="../xbmc/;freetype2/include;../xbmc/lib/boost"
+ PreprocessorDefinitions="WIN32;_DEBUG;_LIB;_MSVC;NOMINMAX;_WIN32_WINNT=0x0501;WINVER=0x0500"
+ StringPooling="FALSE"
+ MinimalRebuild="TRUE"
+ ExceptionHandling="FALSE"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="include.h"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="FALSE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="$(OutDir)/guilib.lib"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="Release (Win32)"
+ IntermediateDirectory="Release (Win32)"
+ ConfigurationType="4"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="../xbmc/;freetype2/include;../xbmc/lib/boost"
+ PreprocessorDefinitions="WIN32;NDEBUG;_LIB;_MSVC;NOMINMAX;_WIN32_WINNT=0x0501;WINVER=0x0500"
+ RuntimeLibrary="0"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="include.h"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="FALSE"
+ DebugInformationFormat="3"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="$(OutDir)/guilib.lib"
+ IgnoreDefaultLibraryNames=""/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Debug (SDL)|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/EHa"
+ Optimization="0"
+ AdditionalIncludeDirectories="../guilib;../xbmc/Win32;../xbmc/;freetype2/include;../xbmc/lib/boost;../xbmc/utils;"../xbmc/lib/libSDL-WIN32/include""
+ PreprocessorDefinitions="WIN32;_DEBUG;_LIB;HAS_SDL;_MSVC;NOMINMAX;_WIN32_WINNT=0x0501;WINVER=0x0500"
+ StringPooling="FALSE"
+ MinimalRebuild="TRUE"
+ ExceptionHandling="FALSE"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ EnableEnhancedInstructionSet="0"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="include.h"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="FALSE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="$(OutDir)/guilib.lib"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release (SDL)|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ GlobalOptimizations="TRUE"
+ FavorSizeOrSpeed="1"
+ AdditionalIncludeDirectories="../guilib;../xbmc/win32;../xbmc;freetype2\include;../xbmc/lib/boost;../xbmc/utils;"../xbmc/lib/libSDL-WIN32/include""
+ PreprocessorDefinitions="WIN32;NDEBUG;_LIB;HAS_SDL;_MSVC;NOMINMAX;_WIN32_WINNT=0x0501;WINVER=0x0500"
+ MinimalRebuild="FALSE"
+ RuntimeLibrary="0"
+ EnableEnhancedInstructionSet="2"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="include.h"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="FALSE"
+ DebugInformationFormat="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="$(OutDir)/guilib.lib"
+ AdditionalLibraryDirectories=""
+ IgnoreAllDefaultLibraries="FALSE"
+ IgnoreDefaultLibraryNames=""/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
+ <File
+ RelativePath=".\AnimatedGif.cpp">
+ </File>
+ <File
+ RelativePath=".\AudioContext.cpp">
+ </File>
+ <File
+ RelativePath=".\DirectXGraphics.cpp">
+ </File>
+ <File
+ RelativePath=".\FrameBufferObject.cpp">
+ </File>
+ <File
+ RelativePath=".\GraphicContext.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIAudioManager.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIBaseContainer.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIBorderedImage.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIButtonControl.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIButtonScroller.cpp">
+ </File>
+ <File
+ RelativePath=".\GUICheckMarkControl.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIColorManager.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIControl.cpp">
+ </File>
+ <File
+ RelativePath=".\GuiControlFactory.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIControlGroup.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIControlGroupList.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIDialog.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIEditControl.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIFadeLabelControl.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIFixedListContainer.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIFont.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIFontManager.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIFontTTF.cpp">
+ </File>
+ <File
+ RelativePath=".\guiImage.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIIncludes.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIInfoColor.cpp">
+ </File>
+ <File
+ RelativePath=".\GUILabelControl.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIListContainer.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIListGroup.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIListItem.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIListItemLayout.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIListLabel.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIMessage.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIMoverControl.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIMultiImage.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIMultiSelectText.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIPanelContainer.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIProgressControl.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIRadioButtonControl.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIResizeControl.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIRSSControl.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIScrollBarControl.cpp">
+ </File>
+ <File
+ RelativePath=".\GUISelectButtonControl.cpp">
+ </File>
+ <File
+ RelativePath=".\GUISettingsSliderControl.cpp">
+ </File>
+ <File
+ RelativePath=".\GUISliderControl.cpp">
+ </File>
+ <File
+ RelativePath=".\GUISound.cpp">
+ </File>
+ <File
+ RelativePath=".\GUISpinControl.cpp">
+ </File>
+ <File
+ RelativePath=".\GUISpinControlEx.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIStandardWindow.cpp">
+ </File>
+ <File
+ RelativePath=".\GUITextBox.cpp">
+ </File>
+ <File
+ RelativePath=".\GUITextLayout.cpp">
+ </File>
+ <File
+ RelativePath=".\GUITexture.cpp">
+ </File>
+ <File
+ RelativePath=".\GUITextureD3D.cpp">
+ </File>
+ <File
+ RelativePath=".\GUITextureGL.cpp">
+ </File>
+ <File
+ RelativePath=".\GUITextureSDL.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIToggleButtonControl.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIVideoControl.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIVisualisationControl.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIWindow.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIWindowManager.cpp">
+ </File>
+ <File
+ RelativePath=".\GUIWrappingListContainer.cpp">
+ </File>
+ <File
+ RelativePath=".\include.cpp">
+ </File>
+ <File
+ RelativePath=".\IWindowManagerCallback.cpp">
+ </File>
+ <File
+ RelativePath=".\Key.cpp">
+ </File>
+ <File
+ RelativePath=".\LocalizeStrings.cpp">
+ </File>
+ <File
+ RelativePath=".\Shader.cpp">
+ </File>
+ <File
+ RelativePath=".\SkinInfo.cpp">
+ </File>
+ <File
+ RelativePath=".\Surface.cpp">
+ </File>
+ <File
+ RelativePath=".\TextureBundle.cpp">
+ </File>
+ <File
+ RelativePath=".\TextureManager.cpp">
+ </File>
+ <File
+ RelativePath=".\VisibleEffect.cpp">
+ </File>
+ <File
+ RelativePath=".\XMLUtils.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
+ <File
+ RelativePath=".\AnimatedGif.h">
+ </File>
+ <File
+ RelativePath=".\AudioContext.h">
+ </File>
+ <File
+ RelativePath=".\DirectXGraphics.h">
+ </File>
+ <File
+ RelativePath=".\FrameBufferObject.h">
+ </File>
+ <File
+ RelativePath=".\Geometry.h">
+ </File>
+ <File
+ RelativePath=".\GraphicContext.h">
+ </File>
+ <File
+ RelativePath=".\gui3d.h">
+ </File>
+ <File
+ RelativePath=".\GUIAudioManager.h">
+ </File>
+ <File
+ RelativePath=".\GUIBaseContainer.h">
+ </File>
+ <File
+ RelativePath=".\GUIBorderedImage.h">
+ </File>
+ <File
+ RelativePath=".\GUIButtonControl.h">
+ </File>
+ <File
+ RelativePath=".\GUIButtonScroller.h">
+ </File>
+ <File
+ RelativePath=".\GUICallback.h">
+ </File>
+ <File
+ RelativePath=".\GUICheckMarkControl.h">
+ </File>
+ <File
+ RelativePath=".\GUIColorManager.h">
+ </File>
+ <File
+ RelativePath=".\GUIControl.h">
+ </File>
+ <File
+ RelativePath=".\GuiControlFactory.h">
+ </File>
+ <File
+ RelativePath=".\GUIControlGroup.h">
+ </File>
+ <File
+ RelativePath=".\GUIControlGroupList.h">
+ </File>
+ <File
+ RelativePath=".\GUIDialog.h">
+ </File>
+ <File
+ RelativePath=".\GUIEditControl.h">
+ </File>
+ <File
+ RelativePath=".\GUIFadeLabelControl.h">
+ </File>
+ <File
+ RelativePath=".\GUIFixedListContainer.h">
+ </File>
+ <File
+ RelativePath=".\GUIFont.h">
+ </File>
+ <File
+ RelativePath=".\GUIFontManager.h">
+ </File>
+ <File
+ RelativePath=".\GUIFontTTF.h">
+ </File>
+ <File
+ RelativePath=".\guiImage.h">
+ </File>
+ <File
+ RelativePath=".\GUIIncludes.h">
+ </File>
+ <File
+ RelativePath=".\GUIInfoColor.h">
+ </File>
+ <File
+ RelativePath=".\GUILabelControl.h">
+ </File>
+ <File
+ RelativePath=".\GUIListContainer.h">
+ </File>
+ <File
+ RelativePath=".\GUIListGroup.h">
+ </File>
+ <File
+ RelativePath=".\GUIListItem.h">
+ </File>
+ <File
+ RelativePath=".\GUIListItemLayout.h">
+ </File>
+ <File
+ RelativePath=".\GUIListLabel.h">
+ </File>
+ <File
+ RelativePath=".\GUIMessage.h">
+ </File>
+ <File
+ RelativePath=".\GUIMoverControl.h">
+ </File>
+ <File
+ RelativePath=".\GUIMultiImage.h">
+ </File>
+ <File
+ RelativePath=".\GUIMultiSelectText.h">
+ </File>
+ <File
+ RelativePath=".\GUIPanelContainer.h">
+ </File>
+ <File
+ RelativePath=".\GUIProgressControl.h">
+ </File>
+ <File
+ RelativePath=".\GUIRadioButtonControl.h">
+ </File>
+ <File
+ RelativePath=".\GUIResizeControl.h">
+ </File>
+ <File
+ RelativePath=".\GUIRSSControl.h">
+ </File>
+ <File
+ RelativePath=".\GUIScrollBarControl.h">
+ </File>
+ <File
+ RelativePath=".\GUISelectButtonControl.h">
+ </File>
+ <File
+ RelativePath=".\GUISettingsSliderControl.h">
+ </File>
+ <File
+ RelativePath=".\GUISliderControl.h">
+ </File>
+ <File
+ RelativePath=".\GUISound.h">
+ </File>
+ <File
+ RelativePath=".\GUISpinControl.h">
+ </File>
+ <File
+ RelativePath=".\GUISpinControlEx.h">
+ </File>
+ <File
+ RelativePath=".\GUIStandardWindow.h">
+ </File>
+ <File
+ RelativePath=".\GUITextBox.h">
+ </File>
+ <File
+ RelativePath=".\GUITextLayout.h">
+ </File>
+ <File
+ RelativePath=".\GUITexture.h">
+ </File>
+ <File
+ RelativePath=".\GUITextureD3D.h">
+ </File>
+ <File
+ RelativePath=".\GUITextureGL.h">
+ </File>
+ <File
+ RelativePath=".\GUITextureSDL.h">
+ </File>
+ <File
+ RelativePath=".\GUIToggleButtonControl.h">
+ </File>
+ <File
+ RelativePath=".\GUIVideoControl.h">
+ </File>
+ <File
+ RelativePath=".\GUIVisualisationControl.h">
+ </File>
+ <File
+ RelativePath=".\GUIWindow.h">
+ </File>
+ <File
+ RelativePath=".\GUIWindowManager.h">
+ </File>
+ <File
+ RelativePath=".\GUIWrappingListContainer.h">
+ </File>
+ <File
+ RelativePath=".\IAudioDeviceChangedCallback.h">
+ </File>
+ <File
+ RelativePath=".\IMsgSenderCallback.h">
+ </File>
+ <File
+ RelativePath=".\IMsgTargetCallback.h">
+ </File>
+ <File
+ RelativePath=".\include.h">
+ </File>
+ <File
+ RelativePath=".\IWindowManagerCallback.h">
+ </File>
+ <File
+ RelativePath=".\Key.h">
+ </File>
+ <File
+ RelativePath=".\LocalizeStrings.h">
+ </File>
+ <File
+ RelativePath=".\Shader.h">
+ </File>
+ <File
+ RelativePath=".\SkinInfo.h">
+ </File>
+ <File
+ RelativePath=".\StdString.h">
+ </File>
+ <File
+ RelativePath=".\Surface.h">
+ </File>
+ <File
+ RelativePath=".\system.h">
+ </File>
+ <File
+ RelativePath=".\TextureBundle.h">
+ </File>
+ <File
+ RelativePath=".\TextureManager.h">
+ </File>
+ <File
+ RelativePath=".\TransformMatrix.h">
+ </File>
+ <File
+ RelativePath=".\Tween.h">
+ </File>
+ <File
+ RelativePath=".\VisibleEffect.h">
+ </File>
+ <File
+ RelativePath=".\XMLUtils.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
+ </Filter>
+ <Filter
+ Name="tinyxml"
+ Filter="">
+ <File
+ RelativePath=".\tinyXML\tinystr.cpp">
+ </File>
+ <File
+ RelativePath=".\tinyXML\tinystr.h">
+ </File>
+ <File
+ RelativePath=".\tinyXML\tinyxml.cpp">
+ </File>
+ <File
+ RelativePath=".\tinyXML\tinyxml.h">
+ </File>
+ <File
+ RelativePath=".\tinyXML\tinyxmlerror.cpp">
+ </File>
+ <File
+ RelativePath=".\tinyXML\tinyxmlparser.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="common"
+ Filter="">
+ <File
+ RelativePath=".\common\DirectInput.cpp">
+ </File>
+ <File
+ RelativePath=".\common\DirectInput.h">
+ </File>
+ <File
+ RelativePath=".\common\DirectInputKeyboard.cpp">
+ </File>
+ <File
+ RelativePath=".\common\DirectInputKeyboard.h">
+ </File>
+ <File
+ RelativePath=".\common\DirectInputMouse.cpp">
+ </File>
+ <File
+ RelativePath=".\common\DirectInputMouse.h">
+ </File>
+ <File
+ RelativePath=".\common\Keyboard.cpp">
+ </File>
+ <File
+ RelativePath=".\common\Keyboard.h">
+ </File>
+ <File
+ RelativePath=".\common\Mouse.cpp">
+ </File>
+ <File
+ RelativePath=".\common\Mouse.h">
+ </File>
+ <File
+ RelativePath=".\common\SDLJoystick.cpp">
+ </File>
+ <File
+ RelativePath=".\common\SDLJoystick.h">
+ </File>
+ <File
+ RelativePath=".\common\SDLKeyboard.cpp">
+ </File>
+ <File
+ RelativePath=".\common\SDLKeyboard.h">
+ </File>
+ <File
+ RelativePath=".\common\SDLMouse.cpp">
+ </File>
+ <File
+ RelativePath=".\common\SDLMouse.h">
+ </File>
+ <Filter
+ Name="IRServerSuite"
+ Filter="">
+ <File
+ RelativePath=".\common\IRServerSuite\IRServerSuite.cpp">
+ </File>
+ <File
+ RelativePath=".\common\IRServerSuite\IRServerSuite.h">
+ </File>
+ <File
+ RelativePath=".\common\IRServerSuite\IrssMessage.cpp">
+ </File>
+ <File
+ RelativePath=".\common\IRServerSuite\IrssMessage.h">
+ </File>
+ </Filter>
+ </Filter>
+ <File
+ RelativePath=".\ReadMe.txt">
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/guilib/system.h b/guilib/system.h new file mode 100644 index 0000000000..c704b94125 --- /dev/null +++ b/guilib/system.h @@ -0,0 +1,191 @@ +#pragma once + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +/***************** + * All platforms + *****************/ +#ifndef HAS_SDL +#define HAS_SDL +#endif + +#define HAS_DVD_DRIVE +#define HAS_DVD_SWSCALE +#define HAS_DVDPLAYER +#define HAS_EVENT_SERVER +#define HAS_KARAOKE +#define HAS_RAR +#define HAS_SCREENSAVER +#define HAS_PYTHON +#define HAS_SHOUTCAST +#define HAS_SYSINFO +#define HAS_UPNP +#define HAS_VIDEO_PLAYBACK +#define HAS_VISUALISATION +#define HAS_WEB_SERVER + +#define HAS_AC3_CODEC +#define HAS_DTS_CODEC +#define HAS_CDDA_RIPPER + +#define HAS_FILESYSTEM +#define HAS_FILESYSTEM_SMB +#define HAS_FILESYSTEM_CDDA +#define HAS_FILESYSTEM_RTV +#define HAS_FILESYSTEM_DAAP +#define HAS_FILESYSTEM_SAP +#define HAS_FILESYSTEM_VTP +#define HAS_FILESYSTEM_HTSP +#define HAS_FILESYSTEM_MMS +#define HAS_CCXSTREAM + +/***************** + * Win32 Specific + *****************/ + +#ifdef _WIN32 +#define HAS_SDL_JOYSTICK +#define HAS_WIN32_NETWORK +#define HAS_LIRC +#define HAS_IRSERVERSUITE // depends on HAS_LIRC define +#define HAS_AUDIO +#endif + +/***************** + * Mac Specific + *****************/ + +#ifdef __APPLE__ +#define HAS_ZEROCONF +#define HAS_GL +#define HAS_LINUX_NETWORK +#define HAS_SDL_AUDIO +#define HAS_SDL_OPENGL +#define HAS_SDL_WIN_EVENTS +#endif + +/***************** + * Linux Specific + *****************/ + +#if defined(_LINUX) && !defined(__APPLE__) +#ifndef HAS_SDL_OPENGL +#define HAS_SDL_OPENGL +#endif +#ifdef HAS_AVAHI +#define HAS_ZEROCONF +#endif +#define HAS_LCD +#define HAS_HAL +#define HAS_DBUS +#define HAS_DBUS_SERVER +#define HAS_GL +#define HAS_GLX +#define HAS_LINUX_NETWORK +#define HAS_SDL_AUDIO +#define HAS_LIRC +#define HAS_SDL_WIN_EVENTS +#endif + +/***************** + * SVN revision + *****************/ + +#ifdef __APPLE__ +#include "../svn_revision.h" +#endif + +#ifndef SVN_REV +#define SVN_REV "Unknown" +#endif + +#if defined(_LINUX) && !defined(__APPLE__) +#include "../config.h" +#endif + +/**************************************** + * Additional platform specific includes + ****************************************/ + +#ifdef _WIN32 +#if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H)) +#include <winsock2.h> +#endif +#include <windows.h> +#define DIRECTINPUT_VERSION 0x0800 +#include "mmsystem.h" +#include "DInput.h" +#include "DSound.h" +#define DSSPEAKER_USE_DEFAULT DSSPEAKER_STEREO +#define LPDIRECTSOUND8 LPDIRECTSOUND +#undef GetFreeSpace +#include "PlatformInclude.h" +#include "D3D9.h" // On Win32, we're always using DirectX for something, whether it be the actual rendering +#include "D3DX9.h" // or the reference video clock. +#ifdef HAS_SDL +#include "SDL\SDL.h" +#endif +#endif + +#ifdef _LINUX +#include <unistd.h> +#include <time.h> +#include <sys/time.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <errno.h> +#include "PlatformInclude.h" +#endif + +#ifdef HAS_GL +#ifdef _WIN32 +#include "GL/glew.h" +#include <GL/gl.h> +#include <GL/glu.h> +//#include <GL/wglext.h> +#elif defined(__APPLE__) +#include <GL/glew.h> +#include <OpenGL/gl.h> +#elif defined(_LINUX) +#include <GL/glew.h> +#include <GL/gl.h> +#endif +#endif + +#if HAS_GLES == 2 +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#endif + +#define SAFE_DELETE(p) { delete (p); (p)=NULL; } +#define SAFE_DELETE_ARRAY(p) { delete[] (p); (p)=NULL; } +#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } + +// Useful pixel colour manipulation macros +#define GET_A(color) ((color >> 24) & 0xFF) +#define GET_R(color) ((color >> 16) & 0xFF) +#define GET_G(color) ((color >> 8) & 0xFF) +#define GET_B(color) ((color >> 0) & 0xFF) + diff --git a/guilib/tinyXML/Makefile b/guilib/tinyXML/Makefile new file mode 100644 index 0000000000..cdb40e5201 --- /dev/null +++ b/guilib/tinyXML/Makefile @@ -0,0 +1,6 @@ +INCLUDES=-I. -I../ -I../../xbmc -I../../xbmc/linux -I../../xbmc/utils +SRCS=tinystr.cpp tinyxml.cpp tinyxmlerror.cpp tinyxmlparser.cpp +LIB=tinyxml.a + +include ../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) diff --git a/guilib/tinyXML/tinystr.cpp b/guilib/tinyXML/tinystr.cpp new file mode 100644 index 0000000000..681250714b --- /dev/null +++ b/guilib/tinyXML/tinystr.cpp @@ -0,0 +1,116 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +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. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Løvset, 7. April 2005. + */ + + +#ifndef TIXML_USE_STL + +#include "tinystr.h" + +// Error value for find primitive +const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1); + + +// Null rep. +TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } }; + + +void TiXmlString::reserve (size_type cap) +{ + if (cap > capacity()) + { + TiXmlString tmp; + tmp.init(length(), cap); + memcpy(tmp.start(), data(), length()); + swap(tmp); + } +} + + +TiXmlString& TiXmlString::assign(const char* str, size_type len) +{ + size_type cap = capacity(); + if (len > cap || cap > 3*(len + 8)) + { + TiXmlString tmp; + tmp.init(len); + memcpy(tmp.start(), str, len); + swap(tmp); + } + else + { + memmove(start(), str, len); + set_size(len); + } + return *this; +} + + +TiXmlString& TiXmlString::append(const char* str, size_type len) +{ + size_type newsize = length() + len; + if (newsize > capacity()) + { + reserve (newsize + capacity()); + } + memmove(finish(), str, len); + set_size(newsize); + return *this; +} + + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) +{ + TiXmlString tmp; + tmp.reserve(a.length() + b.length()); + tmp += a; + tmp += b; + return tmp; +} + +TiXmlString operator + (const TiXmlString & a, const char* b) +{ + TiXmlString tmp; + TiXmlString::size_type b_len = static_cast<TiXmlString::size_type>( strlen(b) ); + tmp.reserve(a.length() + b_len); + tmp += a; + tmp.append(b, b_len); + return tmp; +} + +TiXmlString operator + (const char* a, const TiXmlString & b) +{ + TiXmlString tmp; + TiXmlString::size_type a_len = static_cast<TiXmlString::size_type>( strlen(a) ); + tmp.reserve(a_len + b.length()); + tmp.append(a, a_len); + tmp += b; + return tmp; +} + + +#endif // TIXML_USE_STL diff --git a/guilib/tinyXML/tinystr.h b/guilib/tinyXML/tinystr.h new file mode 100644 index 0000000000..3c2aa9d54d --- /dev/null +++ b/guilib/tinyXML/tinystr.h @@ -0,0 +1,319 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +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. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005. + * + * - completely rewritten. compact, clean, and fast implementation. + * - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems) + * - fixed reserve() to work as per specification. + * - fixed buggy compares operator==(), operator<(), and operator>() + * - fixed operator+=() to take a const ref argument, following spec. + * - added "copy" constructor with length, and most compare operators. + * - added swap(), clear(), size(), capacity(), operator+(). + */ + +#ifndef TIXML_USE_STL + +#ifndef TIXML_STRING_INCLUDED +#define TIXML_STRING_INCLUDED + +#include <assert.h> +#include <string.h> + +/* The support for explicit isn't that universal, and it isn't really + required - it is used to check that the TiXmlString class isn't incorrectly + used. Be nice to old compilers and macro it here: +*/ +#if defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + #define TIXML_EXPLICIT explicit +#elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + #define TIXML_EXPLICIT explicit +#else + #define TIXML_EXPLICIT +#endif + + +/* + TiXmlString is an emulation of a subset of the std::string template. + Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. + Only the member functions relevant to the TinyXML project have been implemented. + The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase + a string and there's no more room, we allocate a buffer twice as big as we need. +*/ +class TiXmlString +{ + public : + // The size type used + typedef size_t size_type; + + // Error value for find primitive + static const size_type npos; // = -1; + + + // TiXmlString empty constructor + TiXmlString () : rep_(&nullrep_) + { + } + + // TiXmlString copy constructor + TiXmlString ( const TiXmlString & copy) : rep_(0) + { + init(copy.length()); + memcpy(start(), copy.data(), length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0) + { + init( static_cast<size_type>( strlen(copy) )); + memcpy(start(), copy, length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0) + { + init(len); + memcpy(start(), str, len); + } + + // TiXmlString destructor + ~TiXmlString () + { + quit(); + } + + // = operator + TiXmlString& operator = (const char * copy) + { + return assign( copy, (size_type)strlen(copy)); + } + + // = operator + TiXmlString& operator = (const TiXmlString & copy) + { + return assign(copy.start(), copy.length()); + } + + + // += operator. Maps to append + TiXmlString& operator += (const char * suffix) + { + return append(suffix, static_cast<size_type>( strlen(suffix) )); + } + + // += operator. Maps to append + TiXmlString& operator += (char single) + { + return append(&single, 1); + } + + // += operator. Maps to append + TiXmlString& operator += (const TiXmlString & suffix) + { + return append(suffix.data(), suffix.length()); + } + + + // Convert a TiXmlString into a null-terminated char * + const char * c_str () const { return rep_->str; } + + // Convert a TiXmlString into a char * (need not be null terminated). + const char * data () const { return rep_->str; } + + // Return the length of a TiXmlString + size_type length () const { return rep_->size; } + + // Alias for length() + size_type size () const { return rep_->size; } + + // Checks if a TiXmlString is empty + bool empty () const { return rep_->size == 0; } + + // Return capacity of string + size_type capacity () const { return rep_->capacity; } + + + // single char extraction + const char& at (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // [] operator + char& operator [] (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // find a char in a string. Return TiXmlString::npos if not found + size_type find (char lookup) const + { + return find(lookup, 0); + } + + // find a char in a string from an offset. Return TiXmlString::npos if not found + size_type find (char tofind, size_type offset) const + { + if (offset >= length()) return npos; + + for (const char* p = c_str() + offset; *p != '\0'; ++p) + { + if (*p == tofind) return static_cast< size_type >( p - c_str() ); + } + return npos; + } + + void clear () + { + //Lee: + //The original was just too strange, though correct: + // TiXmlString().swap(*this); + //Instead use the quit & re-init: + quit(); + init(0,0); + } + + /* Function to reserve a big amount of data when we know we'll need it. Be aware that this + function DOES NOT clear the content of the TiXmlString if any exists. + */ + void reserve (size_type cap); + + TiXmlString& assign (const char* str, size_type len); + + TiXmlString& append (const char* str, size_type len); + + void swap (TiXmlString& other) + { + Rep* r = rep_; + rep_ = other.rep_; + other.rep_ = r; + } + + private: + + void init(size_type sz) { init(sz, sz); } + void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; } + char* start() const { return rep_->str; } + char* finish() const { return rep_->str + rep_->size; } + + struct Rep + { + size_type size, capacity; + char str[1]; + }; + + void init(size_type sz, size_type cap) + { + if (cap) + { + // Lee: the original form: + // rep_ = static_cast<Rep*>(operator new(sizeof(Rep) + cap)); + // doesn't work in some cases of new being overloaded. Switching + // to the normal allocation, although use an 'int' for systems + // that are overly picky about structure alignment. + const size_type bytesNeeded = sizeof(Rep) + cap; + const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); + rep_ = reinterpret_cast<Rep*>( new int[ intsNeeded ] ); + + rep_->str[ rep_->size = sz ] = '\0'; + rep_->capacity = cap; + } + else + { + rep_ = &nullrep_; + } + } + + void quit() + { + if (rep_ != &nullrep_) + { + // The rep_ is really an array of ints. (see the allocator, above). + // Cast it back before delete, so the compiler won't incorrectly call destructors. + delete [] ( reinterpret_cast<int*>( rep_ ) ); + } + } + + Rep * rep_; + static Rep nullrep_; + +} ; + + +inline bool operator == (const TiXmlString & a, const TiXmlString & b) +{ + return ( a.length() == b.length() ) // optimization on some platforms + && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare +} +inline bool operator < (const TiXmlString & a, const TiXmlString & b) +{ + return strcmp(a.c_str(), b.c_str()) < 0; +} + +inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); } +inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; } +inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); } +inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); } + +inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; } +inline bool operator == (const char* a, const TiXmlString & b) { return b == a; } +inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); } +inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); } + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b); +TiXmlString operator + (const TiXmlString & a, const char* b); +TiXmlString operator + (const char* a, const TiXmlString & b); + + +/* + TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. + Only the operators that we need for TinyXML have been developped. +*/ +class TiXmlOutStream : public TiXmlString +{ +public : + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const TiXmlString & in) + { + *this += in; + return *this; + } + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const char * in) + { + *this += in; + return *this; + } + +} ; + +#endif // TIXML_STRING_INCLUDED +#endif // TIXML_USE_STL diff --git a/guilib/tinyXML/tinyxml.cpp b/guilib/tinyXML/tinyxml.cpp new file mode 100644 index 0000000000..595a0914e9 --- /dev/null +++ b/guilib/tinyXML/tinyxml.cpp @@ -0,0 +1,2066 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +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. +*/ + +#include <ctype.h> + +#ifdef TIXML_USE_STL +#include <sstream> +#include <iostream> +#endif + +#include "tinyxml.h" + +#define USE_XBMC_FILESYSTEM + +#ifdef USE_XBMC_FILESYSTEM +bool TiXmlBase::condenseWhiteSpace = false; +#include "FileSystem/File.h" +using namespace XFILE; +#else +bool TiXmlBase::condenseWhiteSpace = true; +#endif + + + +void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString ) +{ + int i=0; + + while( i<(int)str.length() ) + { + unsigned char c = (unsigned char) str[i]; + + if ( c == '&' + && i < ( (int)str.length() - 2 ) + && str[i+1] == '#' + && str[i+2] == 'x' ) + { + // Hexadecimal character reference. + // Pass through unchanged. + // © -- copyright symbol, for example. + // + // The -1 is a bug fix from Rob Laveaux. It keeps + // an overflow from happening if there is no ';'. + // There are actually 2 ways to exit this loop - + // while fails (error case) and break (semicolon found). + // However, there is no mechanism (currently) for + // this function to return an error. + while ( i<(int)str.length()-1 ) + { + outString->append( str.c_str() + i, 1 ); + ++i; + if ( str[i] == ';' ) + break; + } + } + else if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; + } + else if ( c == '<' ) + { + outString->append( entity[1].str, entity[1].strLength ); + ++i; + } + else if ( c == '>' ) + { + outString->append( entity[2].str, entity[2].strLength ); + ++i; + } + else if ( c == '\"' ) + { + outString->append( entity[3].str, entity[3].strLength ); + ++i; + } + else if ( c == '\'' ) + { + outString->append( entity[4].str, entity[4].strLength ); + ++i; + } + else if ( c < 32 ) + { + // Easy pass at non-alpha/numeric/symbol + // Below 32 is symbolic. + char buf[ 32 ]; + + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); + #else + sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); + #endif + + //*ME: warning C4267: convert 'size_t' to 'int' + //*ME: Int-Cast to make compiler happy ... + outString->append( buf, (int)strlen( buf ) ); + ++i; + } + else + { + //char realc = (char) c; + //outString->append( &realc, 1 ); + *outString += (char) c; // somewhat more efficient function call. + ++i; + } + } +} + + +TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() +{ + parent = 0; + type = _type; + firstChild = 0; + lastChild = 0; + prev = 0; + next = 0; +} + + +TiXmlNode::~TiXmlNode() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } +} + + +void TiXmlNode::CopyTo( TiXmlNode* target ) const +{ + target->SetValue (value.c_str() ); + target->userData = userData; +} + + +void TiXmlNode::Clear() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } + + firstChild = 0; + lastChild = 0; +} + + +TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) +{ + assert( node->parent == 0 || node->parent == this ); + assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); + + if ( node->Type() == TiXmlNode::DOCUMENT ) + { + delete node; + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + node->parent = this; + + node->prev = lastChild; + node->next = 0; + + if ( lastChild ) + lastChild->next = node; + else + firstChild = node; // it was an empty list. + + lastChild = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) +{ + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + + return LinkEndChild( node ); +} + + +TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) +{ + if ( !beforeThis || beforeThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->next = beforeThis; + node->prev = beforeThis->prev; + if ( beforeThis->prev ) + { + beforeThis->prev->next = node; + } + else + { + assert( firstChild == beforeThis ); + firstChild = node; + } + beforeThis->prev = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) +{ + if ( !afterThis || afterThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->prev = afterThis; + node->next = afterThis->next; + if ( afterThis->next ) + { + afterThis->next->prev = node; + } + else + { + assert( lastChild == afterThis ); + lastChild = node; + } + afterThis->next = node; + return node; +} + + +TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) +{ + if ( replaceThis->parent != this ) + return 0; + + TiXmlNode* node = withThis.Clone(); + if ( !node ) + return 0; + + node->next = replaceThis->next; + node->prev = replaceThis->prev; + + if ( replaceThis->next ) + replaceThis->next->prev = node; + else + lastChild = node; + + if ( replaceThis->prev ) + replaceThis->prev->next = node; + else + firstChild = node; + + delete replaceThis; + node->parent = this; + return node; +} + + +bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) +{ + if ( removeThis->parent != this ) + { + assert( 0 ); + return false; + } + + if ( removeThis->next ) + removeThis->next->prev = removeThis->prev; + else + lastChild = removeThis->prev; + + if ( removeThis->prev ) + removeThis->prev->next = removeThis->next; + else + firstChild = removeThis->next; + + delete removeThis; + return true; +} + +const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + + +const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +void TiXmlElement::RemoveAttribute( const char * name ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING str( name ); + TiXmlAttribute* node = attributeSet.Find( str ); + #else + TiXmlAttribute* node = attributeSet.Find( name ); + #endif + if ( node ) + { + attributeSet.Remove( node ); + delete node; + } +} + +const TiXmlElement* TiXmlNode::FirstChildElement() const +{ + const TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement() const +{ + const TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlDocument* TiXmlNode::GetDocument() const +{ + const TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + + +TiXmlElement::TiXmlElement (const char * _value) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} + + +#ifdef TIXML_USE_STL +TiXmlElement::TiXmlElement( const std::string& _value ) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} +#endif + + +TiXmlElement::TiXmlElement( const TiXmlElement& copy) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + copy.CopyTo( this ); +} + + +void TiXmlElement::operator=( const TiXmlElement& base ) +{ + ClearThis(); + base.CopyTo( this ); +} + + +TiXmlElement::~TiXmlElement() +{ + ClearThis(); +} + + +void TiXmlElement::ClearThis() +{ + Clear(); + while( attributeSet.First() ) + { + TiXmlAttribute* node = attributeSet.First(); + attributeSet.Remove( node ); + delete node; + } +} + + +const char* TiXmlElement::Attribute( const char* name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return node->Value(); + return 0; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return &node->ValueStr(); + return 0; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, int* i ) const +{ + const char* s = Attribute( name ); + if ( i ) + { + if ( s ) { + *i = atoi( s ); + } + else { + *i = 0; + } + } + return s; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const +{ + const std::string* s = Attribute( name ); + if ( i ) + { + if ( s ) { + *i = atoi( s->c_str() ); + } + else { + *i = 0; + } + } + return s; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, double* d ) const +{ + const char* s = Attribute( name ); + if ( d ) + { + if ( s ) { + *d = atof( s ); + } + else { + *d = 0; + } + } + return s; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const +{ + const std::string* s = Attribute( name ); + if ( d ) + { + if ( s ) { + *d = atof( s->c_str() ); + } + else { + *d = 0; + } + } + return s; +} +#endif + + +int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryIntValue( ival ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryIntValue( ival ); +} +#endif + + +int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryDoubleValue( dval ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryDoubleValue( dval ); +} +#endif + + +void TiXmlElement::SetAttribute( const char * name, int val ) +{ + char buf[64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%d", val ); + #else + sprintf( buf, "%d", val ); + #endif + SetAttribute( name, buf ); +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, int val ) +{ + std::ostringstream oss; + oss << val; + SetAttribute( name, oss.str() ); +} +#endif + + +void TiXmlElement::SetDoubleAttribute( const char * name, double val ) +{ + char buf[256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%f", val ); + #else + sprintf( buf, "%f", val ); + #endif + SetAttribute( name, buf ); +} + + +void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING _name( cname ); + TIXML_STRING _value( cvalue ); + #else + const char* _name = cname; + const char* _value = cvalue; + #endif + + TiXmlAttribute* node = attributeSet.Find( _name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( cname, cvalue ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, const std::string& _value ) +{ + TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( name, _value ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} +#endif + + +void TiXmlElement::Print( FILE* cfile, int depth ) const +{ + int i; + assert( cfile ); + for ( i=0; i<depth; i++ ) { + fprintf( cfile, " " ); + } + + fprintf( cfile, "<%s", value.c_str() ); + + const TiXmlAttribute* attrib; + for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() ) + { + fprintf( cfile, " " ); + attrib->Print( cfile, depth ); + } + + // There are 3 different formatting approaches: + // 1) An element without children is printed as a <foo /> node + // 2) An element with only a text child is printed as <foo> text </foo> + // 3) An element with children is printed on multiple lines. + TiXmlNode* node; + if ( !firstChild ) + { + fprintf( cfile, " />" ); + } + else if ( firstChild == lastChild && firstChild->ToText() ) + { + fprintf( cfile, ">" ); + firstChild->Print( cfile, depth + 1 ); + fprintf( cfile, "</%s>", value.c_str() ); + } + else + { + fprintf( cfile, ">" ); + + for ( node = firstChild; node; node=node->NextSibling() ) + { + if ( !node->ToText() ) + { + fprintf( cfile, "\n" ); + } + node->Print( cfile, depth+1 ); + } + fprintf( cfile, "\n" ); + for( i=0; i<depth; ++i ) { + fprintf( cfile, " " ); + } + fprintf( cfile, "</%s>", value.c_str() ); + } +} + + +void TiXmlElement::CopyTo( TiXmlElement* target ) const +{ + // superclass: + TiXmlNode::CopyTo( target ); + + // Element class: + // Clone the attributes, then clone the children. + const TiXmlAttribute* attribute = 0; + for( attribute = attributeSet.First(); + attribute; + attribute = attribute->Next() ) + { + target->SetAttribute( attribute->Name(), attribute->Value() ); + } + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + +bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this, attributeSet.First() ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +TiXmlNode* TiXmlElement::Clone() const +{ + TiXmlElement* clone = new TiXmlElement( Value() ); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +const char* TiXmlElement::GetText() const +{ + const TiXmlNode* child = this->FirstChild(); + if ( child ) { + const TiXmlText* childText = child->ToText(); + if ( childText ) { + return childText->Value(); + } + } + return 0; +} + + +TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; +#ifdef HAS_ICONV + convertToUtf8 = false; + iconvContext = (iconv_t) -1; +#endif + ClearError(); +} + +TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; +#ifdef HAS_ICONV + convertToUtf8 = false; + iconvContext = (iconv_t) -1; +#endif + value = documentName; + ClearError(); +} + + +#ifdef TIXML_USE_STL +TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; +#ifdef HAS_ICONV + convertToUtf8 = false; + iconvContext = (iconv_t) -1; +#endif + value = documentName; + ClearError(); +} +#endif + + +TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + copy.CopyTo( this ); +} + +TiXmlDocument::~TiXmlDocument() +{ +#ifdef HAS_ICONV + if (iconvContext != (iconv_t) -1) + { + iconv_close(iconvContext); + iconvContext = (iconv_t) -1; + } +#endif +} + +void TiXmlDocument::operator=( const TiXmlDocument& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) +{ + // See STL_STRING_BUG below. + //StringToBuffer buf( value ); + + return LoadFile( Value(), encoding ); +} + + +bool TiXmlDocument::SaveFile() const +{ + // See STL_STRING_BUG below. +// StringToBuffer buf( value ); +// +// if ( buf.buffer && SaveFile( buf.buffer ) ) +// return true; +// +// return false; + return SaveFile( Value() ); +} + +#ifdef USE_XBMC_FILESYSTEM +bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) +{ + // There was a really terrifying little bug here. The code: + // value = filename + // in the STL case, cause the assignment method of the std::string to + // be called. What is strange, is that the std::string had the same + // address as it's c_str() method, and so bad things happen. Looks + // like a bug in the Microsoft STL implementation. + // Add an extra string to avoid the crash. + TIXML_STRING filename( _filename ); + value = filename; + + CFile file; + if (!file.Open(value)) + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Delete the existing data: + Clear(); + location.Clear(); + + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = -1; + __int64 filelen = file.GetLength(); + if (filelen > 0) + length = (long)filelen; + + // We might be streaming it, correct length will be fixed by reading + if( length < 0 ) + length = 1024; + + // Subtle bug here. TinyXml did use fgets. But from the XML spec: + // 2.11 End-of-Line Handling + // <snip> + // <quote> + // ...the XML processor MUST behave as if it normalized all line breaks in external + // parsed entities (including the document entity) on input, before parsing, by translating + // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to + // a single #xA character. + // </quote> + // + // It is not clear fgets does that, and certainly isn't clear it works cross platform. + // Generally, you expect fgets to translate from the convention of the OS to the c/unix + // convention, and not work generally. + + /* + while( fgets( buf, sizeof(buf), file ) ) + { + data += buf; + } + */ + + char* buf = (char*)malloc(length+1); + long pos = 0; + long len; + while( (len = file.Read(buf+pos, length-pos)) > 0 ) { + pos += len; + assert(pos <= length); + if(pos == length) { + length *= 2; + buf = (char*)realloc(buf, length); + } + } + length = pos; + + file.Close(); + + // Strange case, but good to handle up front. + if ( length == 0 ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // If we have a file, assume it is all one big XML file, and read it in. + // The document parser may decide the document ends sooner than the entire file, however. + TIXML_STRING data; + data.reserve( length ); + + const char* lastPos = buf; + const char* p = buf; + + buf[length] = 0; + while( *p ) { + assert( p < (buf+length) ); + if ( *p == 0xa ) { + // Newline character. No special rules for this. Append all the characters + // since the last string, and include the newline. + data.append( lastPos, (p-lastPos+1) ); // append, include the newline + ++p; // move past the newline + lastPos = p; // and point to the new buffer (may be 0) + assert( p <= (buf+length) ); + } + else if ( *p == 0xd ) { + // Carriage return. Append what we have so far, then + // handle moving forward in the buffer. + if ( (p-lastPos) > 0 ) { + data.append( lastPos, p-lastPos ); // do not add the CR + } + data += (char)0xa; // a proper newline + + if ( *(p+1) == 0xa ) { + // Carriage return - new line sequence + p += 2; + lastPos = p; + assert( p <= (buf+length) ); + } + else { + // it was followed by something else...that is presumably characters again. + ++p; + lastPos = p; + assert( p <= (buf+length) ); + } + } + else { + ++p; + } + } + // Handle any left over characters. + if ( p-lastPos ) { + data.append( lastPos, p-lastPos ); + } + free(buf); + buf = 0; + + Parse( data.c_str(), 0, encoding ); + + if ( Error() ) + return false; + else + return true; +} + +bool TiXmlDocument::SaveFile( const char *filename ) const +{ + XFILE::CFile file; + if (file.OpenForWrite(filename, true)) + { + TiXmlPrinter printer; + Accept(&printer); + file.Write(printer.CStr(), printer.Size()); + return true; + } + return false; +} +#else + +bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) +{ + // There was a really terrifying little bug here. The code: + // value = filename + // in the STL case, cause the assignment method of the std::string to + // be called. What is strange, is that the std::string had the same + // address as it's c_str() method, and so bad things happen. Looks + // like a bug in the Microsoft STL implementation. + // Add an extra string to avoid the crash. + TIXML_STRING filename( _filename ); + value = filename; + + // reading in binary mode so that tinyxml can normalize the EOL + FILE* file = fopen( value.c_str(), "rb" ); + if ( file ) + { + bool result = LoadFile( file, encoding ); + fclose( file ); + return result; + } + else + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } +} + +bool TiXmlDocument::SaveFile( const char * filename ) const +{ + // The old c stuff lives on... + FILE* fp = fopen( filename, "w" ); + if ( fp ) + { + bool result = SaveFile( fp ); + fclose( fp ); + return result; + } + return false; +} +#endif + +bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) +{ + if ( !file ) + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Delete the existing data: + Clear(); + location.Clear(); + + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = -1; + if( fseek( file, 0, SEEK_END ) == 0 ) { + length = ftell( file ); + fseek( file, 0, SEEK_SET ); + } + + // We might be streaming it, correct length will be fixed by reading + if( length < 0 ) + length = 1024; + + // Subtle bug here. TinyXml did use fgets. But from the XML spec: + // 2.11 End-of-Line Handling + // <snip> + // <quote> + // ...the XML processor MUST behave as if it normalized all line breaks in external + // parsed entities (including the document entity) on input, before parsing, by translating + // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to + // a single #xA character. + // </quote> + // + // It is not clear fgets does that, and certainly isn't clear it works cross platform. + // Generally, you expect fgets to translate from the convention of the OS to the c/unix + // convention, and not work generally. + + /* + while( fgets( buf, sizeof(buf), file ) ) + { + data += buf; + } + */ + + char* buf = (char*)malloc(length+1); + long pos = 0; + long len; + while( (len = fread(buf+pos, 1, length-pos, file)) > 0 ) { + pos += len; + assert(pos <= length); + if(pos == length) { + length *= 2; + buf = (char*)realloc(buf, length); + } + } + length = pos; + + // Strange case, but good to handle up front. + if ( length == 0 ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // If we have a file, assume it is all one big XML file, and read it in. + // The document parser may decide the document ends sooner than the entire file, however. + TIXML_STRING data; + data.reserve( length ); + + const char* lastPos = buf; + const char* p = buf; + + buf[length] = 0; + while( *p ) { + assert( p < (buf+length) ); + if ( *p == 0xa ) { + // Newline character. No special rules for this. Append all the characters + // since the last string, and include the newline. + data.append( lastPos, (p-lastPos+1) ); // append, include the newline + ++p; // move past the newline + lastPos = p; // and point to the new buffer (may be 0) + assert( p <= (buf+length) ); + } + else if ( *p == 0xd ) { + // Carriage return. Append what we have so far, then + // handle moving forward in the buffer. + if ( (p-lastPos) > 0 ) { + data.append( lastPos, p-lastPos ); // do not add the CR + } + data += (char)0xa; // a proper newline + + if ( *(p+1) == 0xa ) { + // Carriage return - new line sequence + p += 2; + lastPos = p; + assert( p <= (buf+length) ); + } + else { + // it was followed by something else...that is presumably characters again. + ++p; + lastPos = p; + assert( p <= (buf+length) ); + } + } + else { + ++p; + } + } + // Handle any left over characters. + if ( p-lastPos ) { + data.append( lastPos, p-lastPos ); + } + free(buf); + buf = 0; + + Parse( data.c_str(), 0, encoding ); + + if ( Error() ) + return false; + else + return true; +} + +bool TiXmlDocument::SaveFile( FILE* fp ) const +{ + if ( useMicrosoftBOM ) + { + const unsigned char TIXML_UTF_LEAD_0 = 0xefU; + const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; + const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + + fputc( TIXML_UTF_LEAD_0, fp ); + fputc( TIXML_UTF_LEAD_1, fp ); + fputc( TIXML_UTF_LEAD_2, fp ); + } + Print( fp, 0 ); + return (ferror(fp) == 0); +} + + +void TiXmlDocument::CopyTo( TiXmlDocument* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->error = error; + target->errorId = errorId; + target->errorDesc = errorDesc; + target->tabsize = tabsize; + target->errorLocation = errorLocation; + target->useMicrosoftBOM = useMicrosoftBOM; +#ifdef HAS_ICONV + target->convertToUtf8 = convertToUtf8; + target->iconvContext = (iconv_t) -1; +#endif + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlDocument::Clone() const +{ + TiXmlDocument* clone = new TiXmlDocument(); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlDocument::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + node->Print( cfile, depth ); + fprintf( cfile, "\n" ); + } +} + + +bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +const TiXmlAttribute* TiXmlAttribute::Next() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +/* +TiXmlAttribute* TiXmlAttribute::Next() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} +*/ + +const TiXmlAttribute* TiXmlAttribute::Previous() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +/* +TiXmlAttribute* TiXmlAttribute::Previous() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} +*/ + +void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + TIXML_STRING n, v; + + EncodeString( name, &n ); + EncodeString( value, &v ); + + if (value.find ('\"') == TIXML_STRING::npos) { + if ( cfile ) { + fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; + } + } + else { + if ( cfile ) { + fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; + } + } +} + + +int TiXmlAttribute::QueryIntValue( int* ival ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +int TiXmlAttribute::QueryDoubleValue( double* dval ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +void TiXmlAttribute::SetIntValue( int _value ) +{ + char buf [64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); + #else + sprintf (buf, "%d", _value); + #endif + SetValue (buf); +} + +void TiXmlAttribute::SetDoubleValue( double _value ) +{ + char buf [256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%f", _value); + #else + sprintf (buf, "%f", _value); + #endif + SetValue (buf); +} + +int TiXmlAttribute::IntValue() const +{ + return atoi (value.c_str ()); +} + +double TiXmlAttribute::DoubleValue() const +{ + return atof (value.c_str ()); +} + + +TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlComment::operator=( const TiXmlComment& base ) +{ + Clear(); + base.CopyTo( this ); +} + + +void TiXmlComment::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( int i=0; i<depth; i++ ) + { + fprintf( cfile, " " ); + } + fprintf( cfile, "<!--%s-->", value.c_str() ); +} + + +void TiXmlComment::CopyTo( TiXmlComment* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlComment::Clone() const +{ + TiXmlComment* clone = new TiXmlComment(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlText::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + if ( cdata ) + { + int i; + fprintf( cfile, "\n" ); + for ( i=0; i<depth; i++ ) { + fprintf( cfile, " " ); + } + fprintf( cfile, "<![CDATA[%s]]>\n", value.c_str() ); // unformatted output + } + else + { + TIXML_STRING buffer; + EncodeString( value, &buffer ); + fprintf( cfile, "%s", buffer.c_str() ); + } +} + + +void TiXmlText::CopyTo( TiXmlText* target ) const +{ + TiXmlNode::CopyTo( target ); + target->cdata = cdata; +} + + +bool TiXmlText::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlText::Clone() const +{ + TiXmlText* clone = 0; + clone = new TiXmlText( "" ); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDeclaration::TiXmlDeclaration( const char * _version, + const char * _encoding, + const char * _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} + + +#ifdef TIXML_USE_STL +TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} +#endif + + +TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + if ( cfile ) fprintf( cfile, "<?xml " ); + if ( str ) (*str) += "<?xml "; + + if ( !version.empty() ) { + if ( cfile ) fprintf (cfile, "version=\"%s\" ", version.c_str ()); + if ( str ) { (*str) += "version=\""; (*str) += version; (*str) += "\" "; } + } + if ( !encoding.empty() ) { + if ( cfile ) fprintf (cfile, "encoding=\"%s\" ", encoding.c_str ()); + if ( str ) { (*str) += "encoding=\""; (*str) += encoding; (*str) += "\" "; } + } + if ( !standalone.empty() ) { + if ( cfile ) fprintf (cfile, "standalone=\"%s\" ", standalone.c_str ()); + if ( str ) { (*str) += "standalone=\""; (*str) += standalone; (*str) += "\" "; } + } + if ( cfile ) fprintf( cfile, "?>" ); + if ( str ) (*str) += "?>"; +} + + +void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->version = version; + target->encoding = encoding; + target->standalone = standalone; +} + + +bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlDeclaration::Clone() const +{ + TiXmlDeclaration* clone = new TiXmlDeclaration(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlUnknown::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i<depth; i++ ) + fprintf( cfile, " " ); + fprintf( cfile, "<%s>", value.c_str() ); +} + + +void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlUnknown::Clone() const +{ + TiXmlUnknown* clone = new TiXmlUnknown(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlAttributeSet::TiXmlAttributeSet() +{ + sentinel.next = &sentinel; + sentinel.prev = &sentinel; +} + + +TiXmlAttributeSet::~TiXmlAttributeSet() +{ + assert( sentinel.next == &sentinel ); + assert( sentinel.prev == &sentinel ); +} + + +void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) +{ + #ifdef TIXML_USE_STL + assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. + #else + assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. + #endif + + addMe->next = &sentinel; + addMe->prev = sentinel.prev; + + sentinel.prev->next = addMe; + sentinel.prev = addMe; +} + +void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node == removeMe ) + { + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; + return; + } + } + assert( 0 ); // we tried to remove a non-linked attribute. +} + + +#ifdef TIXML_USE_STL +const TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const +{ + for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +/* +TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} +*/ +#endif + + +const TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const +{ + for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} + +/* +TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} +*/ + +#ifdef TIXML_USE_STL +std::istream& operator>> (std::istream & in, TiXmlNode & base) +{ + TIXML_STRING tag; + tag.reserve( 8 * 1000 ); + base.StreamIn( &in, &tag ); + + base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); + return in; +} +#endif + + +#ifdef TIXML_USE_STL +std::ostream& operator<< (std::ostream & out, const TiXmlNode & base) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out << printer.Str(); + + return out; +} + + +std::string& operator<< (std::string& out, const TiXmlNode& base ) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out.append( printer.Str() ); + + return out; +} +#endif + + +TiXmlHandle TiXmlHandle::FirstChild() const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement() const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild(); + for ( i=0; + child && i<count; + child = child->NextSibling(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild( value ); + for ( i=0; + child && i<count; + child = child->NextSibling( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement(); + for ( i=0; + child && i<count; + child = child->NextSiblingElement(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement( value ); + for ( i=0; + child && i<count; + child = child->NextSiblingElement( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +bool TiXmlPrinter::VisitEnter( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitExit( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + DoIndent(); + buffer += "<"; + buffer += element.Value(); + + for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) + { + buffer += " "; + attrib->Print( 0, 0, &buffer ); + } + + if ( !element.FirstChild() ) + { + buffer += " />"; + DoLineBreak(); + } + else + { + buffer += ">"; + if ( element.FirstChild()->ToText() + && element.LastChild() == element.FirstChild() + && element.FirstChild()->ToText()->CDATA() == false ) + { + simpleTextPrint = true; + // no DoLineBreak()! + } + else + { + DoLineBreak(); + } + } + ++depth; + return true; +} + + +bool TiXmlPrinter::VisitExit( const TiXmlElement& element ) +{ + --depth; + if ( !element.FirstChild() ) + { + // nothing. + } + else + { + if ( simpleTextPrint ) + { + simpleTextPrint = false; + } + else + { + DoIndent(); + } + buffer += "</"; + buffer += element.Value(); + buffer += ">"; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlText& text ) +{ + if ( text.CDATA() ) + { + DoIndent(); + buffer += "<![CDATA["; + buffer += text.Value(); + buffer += "]]>"; + DoLineBreak(); + } + else if ( simpleTextPrint ) + { + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + } + else + { + DoIndent(); + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration ) +{ + DoIndent(); + declaration.Print( 0, 0, &buffer ); + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlComment& comment ) +{ + DoIndent(); + buffer += "<!--"; + buffer += comment.Value(); + buffer += "-->"; + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown ) +{ + DoIndent(); + buffer += "<"; + buffer += unknown.Value(); + buffer += ">"; + DoLineBreak(); + return true; +} + diff --git a/guilib/tinyXML/tinyxml.h b/guilib/tinyXML/tinyxml.h new file mode 100644 index 0000000000..a1619d864a --- /dev/null +++ b/guilib/tinyXML/tinyxml.h @@ -0,0 +1,1830 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +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. +*/ + + +#ifndef TINYXML_INCLUDED +#define TINYXML_INCLUDED + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +// Help out windows (but don't mess up OSX): +#if defined( _WIN32PC) || defined( _WIN32 ) || defined(WIN32) +#if defined( _DEBUG ) && !defined( DEBUG ) +#define DEBUG +#endif +#endif + +#ifndef TIXML_USE_STL +#define TIXML_USE_STL +#endif + +#ifndef HAS_ICONV +#define HAS_ICONV +#endif + +#ifdef TIXML_USE_STL + #include <string> + #include <iostream> + #include <sstream> + #define TIXML_STRING std::string +#else + #include "tinystr.h" + #define TIXML_STRING TiXmlString +#endif + +#ifdef HAS_ICONV +#if defined( _WIN32PC) || defined( _WIN32 ) || defined(WIN32) +#include "lib/libiconv/iconv.h" +#else +#include <iconv.h> +#endif +#endif + +// Deprecated library function hell. Compilers want to use the +// new safe versions. This probably doesn't fully address the problem, +// but it gets closer. There are too many compilers for me to fully +// test. If you get compilation troubles, undefine TIXML_SAFE +#define TIXML_SAFE + +#ifdef TIXML_SAFE + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + // Microsoft visual studio, version 2005 and higher. + #define TIXML_SNPRINTF _snprintf_s + #define TIXML_SNSCANF _snscanf_s + #define TIXML_SSCANF sscanf_s + #elif defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + //#pragma message( "Using _sn* functions." ) + #define TIXML_SNPRINTF _snprintf + #define TIXML_SNSCANF _snscanf + #define TIXML_SSCANF sscanf + #elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_SNSCANF snscanf + #define TIXML_SSCANF sscanf + #else + #define TIXML_SSCANF sscanf + #endif +#endif + +class TiXmlDocument; +class TiXmlElement; +class TiXmlComment; +class TiXmlUnknown; +class TiXmlAttribute; +class TiXmlText; +class TiXmlDeclaration; +class TiXmlParsingData; + +const int TIXML_MAJOR_VERSION = 2; +const int TIXML_MINOR_VERSION = 5; +const int TIXML_PATCH_VERSION = 3; + +/* Internal structure for tracking location of items + in the XML file. +*/ +struct TiXmlCursor +{ + TiXmlCursor() { Clear(); } + void Clear() { row = col = -1; } + + int row; // 0 based. + int col; // 0 based. +}; + + +/** + If you call the Accept() method, it requires being passed a TiXmlVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves + are simple called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, <b>no children of this node or its sibilings</b> will be Visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting. + + You should never change the document from a callback. + + @sa TiXmlNode::Accept() +*/ +class TiXmlVisitor +{ +public: + virtual ~TiXmlVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; } + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; } + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; } + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; } + + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; } + /// Visit a text node + virtual bool Visit( const TiXmlText& /*text*/ ) { return true; } + /// Visit a comment node + virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; } + /// Visit an unknow node + virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; } +}; + +// Only used by Attribute::Query functions +enum +{ + TIXML_SUCCESS, + TIXML_NO_ATTRIBUTE, + TIXML_WRONG_TYPE +}; + + +// Used by the parsing routines. +enum TiXmlEncoding +{ + TIXML_ENCODING_UNKNOWN, + TIXML_ENCODING_UTF8, + TIXML_ENCODING_LEGACY +}; + +const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; + +/** TiXmlBase is a base class for every class in TinyXml. + It does little except to establish that TinyXml classes + can be printed and provide some utility functions. + + In XML, the document and elements can contain + other elements and other types of nodes. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + A Decleration contains: Attributes (not on tree) + @endverbatim +*/ +class TiXmlBase +{ + friend class TiXmlNode; + friend class TiXmlElement; + friend class TiXmlDocument; + +public: + TiXmlBase() : userData(0) {} + virtual ~TiXmlBase() {} + + /** All TinyXml classes can print themselves to a filestream + or the string class (TiXmlString in non-STL mode, std::string + in STL mode.) Either or both cfile and str can be null. + + This is a formatted print, and will insert + tabs and newlines. + + (For an unformatted stream, use the << operator.) + */ + virtual void Print( FILE* cfile, int depth ) const = 0; + + /** The world does not agree on whether white space should be kept or + not. In order to make everyone happy, these global, static functions + are provided to set whether or not TinyXml will condense all white space + into a single space or not. The default is to condense. Note changing this + value is not thread safe. + */ + static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } + + /// Return the current white space setting. + static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } + + /** Return the position, in the original source file, of this node or attribute. + The row and column are 1-based. (That is the first row and first column is + 1,1). If the returns values are 0 or less, then the parser does not have + a row and column value. + + Generally, the row and column value will be set when the TiXmlDocument::Load(), + TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set + when the DOM was created from operator>>. + + The values reflect the initial load. Once the DOM is modified programmatically + (by adding or changing nodes and attributes) the new values will NOT update to + reflect changes in the document. + + There is a minor performance cost to computing the row and column. Computation + can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. + + @sa TiXmlDocument::SetTabSize() + */ + int Row() const { return location.row + 1; } + int Column() const { return location.col + 1; } ///< See Row() + + void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data. + void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. + const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. + + // Table that returs, for a given lead byte, the total number of bytes + // in the UTF-8 sequence. + static const int utf8ByteTable[256]; + + virtual const char* Parse( const char* p, + TiXmlParsingData* data, + TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; + + /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc, + or they will be transformed into entities! + */ + static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out ); + + enum + { + TIXML_NO_ERROR = 0, + TIXML_ERROR, + TIXML_ERROR_OPENING_FILE, + TIXML_ERROR_OUT_OF_MEMORY, + TIXML_ERROR_PARSING_ELEMENT, + TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, + TIXML_ERROR_READING_ELEMENT_VALUE, + TIXML_ERROR_READING_ATTRIBUTES, + TIXML_ERROR_PARSING_EMPTY, + TIXML_ERROR_READING_END_TAG, + TIXML_ERROR_PARSING_UNKNOWN, + TIXML_ERROR_PARSING_COMMENT, + TIXML_ERROR_PARSING_DECLARATION, + TIXML_ERROR_DOCUMENT_EMPTY, + TIXML_ERROR_EMBEDDED_NULL, + TIXML_ERROR_PARSING_CDATA, + TIXML_ERROR_DOCUMENT_TOP_ONLY, + + TIXML_ERROR_STRING_COUNT + }; + +protected: + +#ifdef HAS_ICONV + static void ConvertToUtf8(TiXmlDocument* document, TIXML_STRING* text); +#endif + + static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); + inline static bool IsWhiteSpace( char c ) + { + return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); + } + inline static bool IsWhiteSpace( int c ) + { + if ( c < 256 ) + return IsWhiteSpace( (char) c ); + return false; // Again, only truly correct for English/Latin...but usually works. + } + + #ifdef TIXML_USE_STL + static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ); + static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag ); + #endif + + /* Reads an XML name into the string provided. Returns + a pointer just past the last character of the name, + or 0 if the function has an error. + */ + static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); + + /* Reads text. Returns a pointer past the given end tag. + Wickedly complex options, but it keeps the (sensitive) code in one place. + */ + static const char* ReadText( const char* in, // where to start + TIXML_STRING* text, // the string read + bool ignoreWhiteSpace, // whether to keep the white space + const char* endTag, // what ends this text + bool ignoreCase, // whether to ignore case in the end tag + TiXmlEncoding encoding ); // the current encoding + + // If an entity has been found, transform it into a character. + static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); + + // Get a character, while interpreting entities. + // The length can be from 0 to 4 bytes. + inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) + { + assert( p ); + if ( encoding == TIXML_ENCODING_UTF8 ) + { + *length = utf8ByteTable[ *((const unsigned char*)p) ]; + assert( *length >= 0 && *length < 5 ); + } + else + { + *length = 1; + } + + if ( *length == 1 ) + { + if ( *p == '&' ) + return GetEntity( p, _value, length, encoding ); + *_value = *p; + return p+1; + } + else if ( *length ) + { + //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), + // and the null terminator isn't needed + for( int i=0; p[i] && i<*length; ++i ) { + _value[i] = p[i]; + } + return p + (*length); + } + else + { + // Not valid text. + return 0; + } + } + + // Return true if the next characters in the stream are any of the endTag sequences. + // Ignore case only works for english, and should only be relied on when comparing + // to English words: StringEqual( p, "version", true ) is fine. + static bool StringEqual( const char* p, + const char* endTag, + bool ignoreCase, + TiXmlEncoding encoding ); + + static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; + + TiXmlCursor location; + + /// Field containing a generic user pointer + void* userData; + + // None of these methods are reliable for any language except English. + // Good for approximation, not great for accuracy. + static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); + static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); + inline static int ToLower( int v, TiXmlEncoding encoding ) + { + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( v < 128 ) return tolower( v ); + return v; + } + else + { + return tolower( v ); + } + } + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + +private: + TiXmlBase( const TiXmlBase& ); // not implemented. + void operator=( const TiXmlBase& base ); // not allowed. + + struct Entity + { + const char* str; + unsigned int strLength; + char chr; + }; + enum + { + NUM_ENTITY = 5, + MAX_ENTITY_LENGTH = 6 + + }; + static Entity entity[ NUM_ENTITY ]; + static bool condenseWhiteSpace; +}; + + +/** The parent class for everything in the Document Object Model. + (Except for attributes). + Nodes have siblings, a parent, and children. A node can be + in a document, or stand on its own. The type of a TiXmlNode + can be queried, and it can be cast to its more defined type. +*/ +class TiXmlNode : public TiXmlBase +{ + friend class TiXmlDocument; + friend class TiXmlElement; + +public: + #ifdef TIXML_USE_STL + + /** An input stream operator, for every class. Tolerant of newlines and + formatting, but doesn't expect them. + */ + friend std::istream& operator >> (std::istream& in, TiXmlNode& base); + + /** An output stream operator, for every class. Note that this outputs + without any newlines or formatting, as opposed to Print(), which + includes tabs and new lines. + + The operator<< and operator>> are not completely symmetric. Writing + a node to a stream is very well defined. You'll get a nice stream + of output, without any extra whitespace or newlines. + + But reading is not as well defined. (As it always is.) If you create + a TiXmlElement (for example) and read that from an input stream, + the text needs to define an element or junk will result. This is + true of all input streams, but it's worth keeping in mind. + + A TiXmlDocument will read nodes until it reads a root element, and + all the children of that root element. + */ + friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); + + /// Appends the XML node or attribute to a std::string. + friend std::string& operator<< (std::string& out, const TiXmlNode& base ); + + #endif + + /** The types of XML nodes supported by TinyXml. (All the + unsupported types are picked up by UNKNOWN.) + */ + enum NodeType + { + DOCUMENT, + ELEMENT, + COMMENT, + UNKNOWN, + TEXT, + DECLARATION, + TYPECOUNT + }; + + virtual ~TiXmlNode(); + + /** The meaning of 'value' changes for the specific type of + TiXmlNode. + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + + The subclasses will wrap this function. + */ + const char *Value() const { return value.c_str (); } + + #ifdef TIXML_USE_STL + /** Return Value() as a std::string. If you only use STL, + this is more efficient than calling Value(). + Only available in STL mode. + */ + const std::string& ValueStr() const { return value; } + #endif + + const TIXML_STRING& ValueTStr() const { return value; } + + /** Changes the value of the node. Defined as: + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + void SetValue(const char * _value) { value = _value;} + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Delete all the children of this node. Does not affect 'this'. + void Clear(); + + /// One step up the DOM. + TiXmlNode* Parent() { return parent; } + const TiXmlNode* Parent() const { return parent; } + + const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. + TiXmlNode* FirstChild() { return firstChild; } + const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. + /// The first child of this node with the matching 'value'. Will be null if none found. + TiXmlNode* FirstChild( const char * _value ) { + // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe) + // call the method, cast the return back to non-const. + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value )); + } + const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. + TiXmlNode* LastChild() { return lastChild; } + + const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. + TiXmlNode* LastChild( const char * _value ) { + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value )); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. + #endif + + /** An alternate way to walk the children of a node. + One way to iterate over nodes is: + @verbatim + for( child = parent->FirstChild(); child; child = child->NextSibling() ) + @endverbatim + + IterateChildren does the same thing with the syntax: + @verbatim + child = 0; + while( child = parent->IterateChildren( child ) ) + @endverbatim + + IterateChildren takes the previous child as input and finds + the next one. If the previous child is null, it returns the + first. IterateChildren will return null when done. + */ + const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) ); + } + + /// This flavor of IterateChildren searches for children with a particular 'value' + const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + #endif + + /** Add a new node related to this. Adds a child past the LastChild. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); + + + /** Add a new node related to this. Adds a child past the LastChild. + + NOTE: the node to be added is passed by pointer, and will be + henceforth owned (and deleted) by tinyXml. This method is efficient + and avoids an extra copy, but should be used with care as it + uses a different memory model than the other insert functions. + + @sa InsertEndChild + */ + TiXmlNode* LinkEndChild( TiXmlNode* addThis ); + + /** Add a new node related to this. Adds a child before the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); + + /** Add a new node related to this. Adds a child after the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); + + /** Replace a child of this node. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); + + /// Delete a child of this node. + bool RemoveChild( TiXmlNode* removeThis ); + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling() const { return prev; } + TiXmlNode* PreviousSibling() { return prev; } + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling( const char * ) const; + TiXmlNode* PreviousSibling( const char *_prev ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Navigate to a sibling node. + const TiXmlNode* NextSibling() const { return next; } + TiXmlNode* NextSibling() { return next; } + + /// Navigate to a sibling node with the given 'value'. + const TiXmlNode* NextSibling( const char * ) const; + TiXmlNode* NextSibling( const char* _next ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement() const; + TiXmlElement* NextSiblingElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement( const char * ) const; + TiXmlElement* NextSiblingElement( const char *_next ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement() const; + TiXmlElement* FirstChildElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() ); + } + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement( const char * _value ) const; + TiXmlElement* FirstChildElement( const char * _value ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /** Query the type (as an enumerated value, above) of this node. + The possible types are: DOCUMENT, ELEMENT, COMMENT, + UNKNOWN, TEXT, and DECLARATION. + */ + int Type() const { return type; } + + /** Return a pointer to the Document this node lives in. + Returns null if not in a document. + */ + const TiXmlDocument* GetDocument() const; + TiXmlDocument* GetDocument() { + return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() ); + } + + /// Returns true if this node has no children. + bool NoChildren() const { return !firstChild; } + + virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + /** Create an exact duplicate of this node and return it. The memory must be deleted + by the caller. + */ + virtual TiXmlNode* Clone() const = 0; + + /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the TiXmlVisitor interface. + + This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + TiXmlPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( TiXmlVisitor* visitor ) const = 0; + +protected: + TiXmlNode( NodeType _type ); + + // Copy to the allocated object. Shared functionality between Clone, Copy constructor, + // and the assignment operator. + void CopyTo( TiXmlNode* target ) const; + + #ifdef TIXML_USE_STL + // The real work of the input operator. + virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0; + #endif + + // Figure out what is at *p, and parse it. Returns null if it is not an xml node. + TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); + + TiXmlNode* parent; + NodeType type; + + TiXmlNode* firstChild; + TiXmlNode* lastChild; + + TIXML_STRING value; + + TiXmlNode* prev; + TiXmlNode* next; + +private: + TiXmlNode( const TiXmlNode& ); // not implemented. + void operator=( const TiXmlNode& base ); // not allowed. +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not TiXmlNodes, since they are not + part of the tinyXML document object model. There are other + suggested ways to look at this problem. +*/ +class TiXmlAttribute : public TiXmlBase +{ + friend class TiXmlAttributeSet; + +public: + /// Construct an empty attribute. + TiXmlAttribute() : TiXmlBase() + { + document = 0; + prev = next = 0; + } + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlAttribute( const std::string& _name, const std::string& _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + #endif + + /// Construct an attribute with a name and value. + TiXmlAttribute( const char * _name, const char * _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + + const char* Name() const { return name.c_str(); } ///< Return the name of this attribute. + const char* Value() const { return value.c_str(); } ///< Return the value of this attribute. + #ifdef TIXML_USE_STL + const std::string& ValueStr() const { return value; } ///< Return the value of this attribute. + #endif + int IntValue() const; ///< Return the value of this attribute, converted to an integer. + double DoubleValue() const; ///< Return the value of this attribute, converted to a double. + + // Get the tinyxml string representation + const TIXML_STRING& NameTStr() const { return name; } + + /** QueryIntValue examines the value string. It is an alternative to the + IntValue() method with richer error checking. + If the value is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. + + A specialized but useful call. Note that for success it returns 0, + which is the opposite of almost all other TinyXml calls. + */ + int QueryIntValue( int* _value ) const; + /// QueryDoubleValue examines the value string. See QueryIntValue(). + int QueryDoubleValue( double* _value ) const; + + void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. + void SetValue( const char* _value ) { value = _value; } ///< Set the value. + + void SetIntValue( int _value ); ///< Set the value from an integer. + void SetDoubleValue( double _value ); ///< Set the value from a double. + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetName( const std::string& _name ) { name = _name; } + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Get the next sibling attribute in the DOM. Returns null at end. + const TiXmlAttribute* Next() const; + TiXmlAttribute* Next() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); + } + + /// Get the previous sibling attribute in the DOM. Returns null at beginning. + const TiXmlAttribute* Previous() const; + TiXmlAttribute* Previous() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); + } + + bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } + bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } + bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } + + /* Attribute parsing starts: first letter of the name + returns: the next char after the value end quote + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + // Prints this Attribute to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + + // [internal use] + // Set the document pointer so the attribute can report errors. + void SetDocument( TiXmlDocument* doc ) { document = doc; } + +private: + TiXmlAttribute( const TiXmlAttribute& ); // not implemented. + void operator=( const TiXmlAttribute& base ); // not allowed. + + TiXmlDocument* document; // A pointer back to a document, for error reporting. + TIXML_STRING name; + TIXML_STRING value; + TiXmlAttribute* prev; + TiXmlAttribute* next; +}; + + +/* A class used to manage a group of attributes. + It is only used internally, both by the ELEMENT and the DECLARATION. + + The set can be changed transparent to the Element and Declaration + classes that use it, but NOT transparent to the Attribute + which has to implement a next() and previous() method. Which makes + it a bit problematic and prevents the use of STL. + + This version is implemented with circular lists because: + - I like circular lists + - it demonstrates some independence from the (typical) doubly linked list. +*/ +class TiXmlAttributeSet +{ +public: + TiXmlAttributeSet(); + ~TiXmlAttributeSet(); + + void Add( TiXmlAttribute* attribute ); + void Remove( TiXmlAttribute* attribute ); + + const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + + const TiXmlAttribute* Find( const char* _name ) const; + TiXmlAttribute* Find( const char* _name ) { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) ); + } + #ifdef TIXML_USE_STL + const TiXmlAttribute* Find( const std::string& _name ) const; + TiXmlAttribute* Find( const std::string& _name ) { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) ); + } + + #endif + +private: + //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), + //*ME: this class must be also use a hidden/disabled copy-constructor !!! + TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed + void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) + + TiXmlAttribute sentinel; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TiXmlElement : public TiXmlNode +{ +public: + /// Construct an element. + TiXmlElement (const char * in_value); + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlElement( const std::string& _value ); + #endif + + TiXmlElement( const TiXmlElement& ); + + void operator=( const TiXmlElement& base ); + + virtual ~TiXmlElement(); + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + */ + const char* Attribute( const char* name ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an integer, + the integer value will be put in the return 'i', if 'i' + is non-null. + */ + const char* Attribute( const char* name, int* i ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an double, + the double value will be put in the return 'd', if 'd' + is non-null. + */ + const char* Attribute( const char* name, double* d ) const; + + /** QueryIntAttribute examines the attribute - it is an alternative to the + Attribute() method with richer error checking. + If the attribute is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. If the attribute + does not exist, then TIXML_NO_ATTRIBUTE is returned. + */ + int QueryIntAttribute( const char* name, int* _value ) const; + /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, double* _value ) const; + /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). + int QueryFloatAttribute( const char* name, float* _value ) const { + double d; + int result = QueryDoubleAttribute( name, &d ); + if ( result == TIXML_SUCCESS ) { + *_value = (float)d; + } + return result; + } + + #ifdef TIXML_USE_STL + /** Template form of the attribute query which will try to read the + attribute into the specified type. Very easy, very powerful, but + be careful to make sure to call this with the correct type. + + NOTE: This method doesn't work correctly for 'string' types. + + @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE + */ + template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + std::stringstream sstream( node->ValueStr() ); + sstream >> *outValue; + if ( !sstream.fail() ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; + } + /* + This is - in theory - a bug fix for "QueryValueAtribute returns truncated std::string" + but template specialization is hard to get working cross-compiler. Leaving the bug for now. + + // The above will fail for std::string because the space character is used as a seperator. + // Specialize for strings. Bug [ 1695429 ] QueryValueAtribute returns truncated std::string + template<> int QueryValueAttribute( const std::string& name, std::string* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + *outValue = node->ValueStr(); + return TIXML_SUCCESS; + } + */ + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char* name, const char * _value ); + + #ifdef TIXML_USE_STL + const std::string* Attribute( const std::string& name ) const; + const std::string* Attribute( const std::string& name, int* i ) const; + const std::string* Attribute( const std::string& name, double* d ) const; + int QueryIntAttribute( const std::string& name, int* _value ) const; + int QueryDoubleAttribute( const std::string& name, double* _value ) const; + + /// STL std::string form. + void SetAttribute( const std::string& name, const std::string& _value ); + ///< STL std::string form. + void SetAttribute( const std::string& name, int _value ); + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char * name, int value ); + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetDoubleAttribute( const char * name, double value ); + + /** Deletes an attribute with the given name. + */ + void RemoveAttribute( const char * name ); + #ifdef TIXML_USE_STL + void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. + #endif + + const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. + TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } + const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. + TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the TiXmlText child + and accessing it directly. + + If the first child of 'this' is a TiXmlText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + <foo>This is text</foo> + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + <foo><b>This is text</b></foo> + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + <foo>This is <b>text</b></foo> + @endverbatim + GetText() will return "This is ". + + WARNING: GetText() accesses a child node - don't become confused with the + similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are + safe type casts on the referenced node. + */ + const char* GetText() const; + + /// Creates a new Element and returns it - the returned element is a copy. + virtual TiXmlNode* Clone() const; + // Print the Element to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: next char past '<' + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + + void CopyTo( TiXmlElement* target ) const; + void ClearThis(); // like clear, but initializes 'this' object as well + + // Used to be public [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + /* [internal use] + Reads the "value" of the element -- another element, or text. + This should terminate with the current end tag. + */ + const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +private: + + TiXmlAttributeSet attributeSet; +}; + + +/** An XML comment. +*/ +class TiXmlComment : public TiXmlNode +{ +public: + /// Constructs an empty comment. + TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {} + /// Construct a comment from text. + TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::COMMENT ) { + SetValue( _value ); + } + TiXmlComment( const TiXmlComment& ); + void operator=( const TiXmlComment& base ); + + virtual ~TiXmlComment() {} + + /// Returns a copy of this Comment. + virtual TiXmlNode* Clone() const; + // Write this Comment to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: at the ! of the !-- + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlComment* target ) const; + + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif +// virtual void StreamOut( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** XML text. A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCDATA() and query it with CDATA(). +*/ +class TiXmlText : public TiXmlNode +{ + friend class TiXmlElement; +public: + /** Constructor for text element. By default, it is treated as + normal, encoded text. If you want it be output as a CDATA text + element, set the parameter _cdata to 'true' + */ + TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + virtual ~TiXmlText() {} + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + #endif + + TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT ) { copy.CopyTo( this ); } + void operator=( const TiXmlText& base ) { base.CopyTo( this ); } + + // Write this text object to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /// Queries whether this represents text using a CDATA section. + bool CDATA() const { return cdata; } + /// Turns on or off a CDATA representation of text. + void SetCDATA( bool _cdata ) { cdata = _cdata; } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + /// [internal use] Creates a new Element and returns it. + virtual TiXmlNode* Clone() const; + void CopyTo( TiXmlText* target ) const; + + bool Blank() const; // returns true if all white space and new lines + // [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + bool cdata; // true if this should be input and output as a CDATA style text element +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + <?xml version="1.0" standalone="yes"?> + @endverbatim + + TinyXml will happily read or write files without a declaration, + however. There are 3 possible attributes to the declaration: + version, encoding, and standalone. + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 3 and they are always the same. +*/ +class TiXmlDeclaration : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlDeclaration() : TiXmlNode( TiXmlNode::DECLARATION ) {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ); +#endif + + /// Construct. + TiXmlDeclaration( const char* _version, + const char* _encoding, + const char* _standalone ); + + TiXmlDeclaration( const TiXmlDeclaration& copy ); + void operator=( const TiXmlDeclaration& copy ); + + virtual ~TiXmlDeclaration() {} + + /// Version. Will return an empty string if none was found. + const char *Version() const { return version.c_str (); } + /// Encoding. Will return an empty string if none was found. + const char *Encoding() const { return encoding.c_str (); } + /// Is this a standalone document? + const char *Standalone() const { return standalone.c_str (); } + + /// Creates a copy of this Declaration and returns it. + virtual TiXmlNode* Clone() const; + // Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlDeclaration* target ) const; + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + + TIXML_STRING version; + TIXML_STRING encoding; + TIXML_STRING standalone; +}; + + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. +*/ +class TiXmlUnknown : public TiXmlNode +{ +public: + TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN ) {} + virtual ~TiXmlUnknown() {} + + TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN ) { copy.CopyTo( this ); } + void operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); } + + /// Creates a copy of this Unknown and returns it. + virtual TiXmlNode* Clone() const; + // Print this Unknown to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected: + void CopyTo( TiXmlUnknown* target ) const; + + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + +}; + + +/** Always the top level node. A document binds together all the + XML pieces. It can be saved, loaded, and printed to the screen. + The 'value' of a document node is the xml file name. +*/ +class TiXmlDocument : public TiXmlNode +{ +public: + /// Create an empty document, that has no name. + TiXmlDocument(); + /// Create a document with a name. The name of the document is also the filename of the xml. + TiXmlDocument( const char * documentName ); + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlDocument( const std::string& documentName ); + #endif + + TiXmlDocument( const TiXmlDocument& copy ); + void operator=( const TiXmlDocument& copy ); + + virtual ~TiXmlDocument(); + + /** Load a file using the current document value. + Returns true if successful. Will delete any existing + document data before loading. + */ + bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the current document value. Returns true if successful. + bool SaveFile() const; + /// Load a file using the given filename. Returns true if successful. + bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given filename. Returns true if successful. + bool SaveFile( const char * filename ) const; + /** Load a file using the given FILE*. Returns true if successful. Note that this method + doesn't stream - the entire object pointed at by the FILE* + will be interpreted as an XML file. TinyXML doesn't stream in XML from the current + file location. Streaming may be added in the future. + */ + bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given FILE*. Returns true if successful. + bool SaveFile( FILE* ) const; + + #ifdef TIXML_USE_STL + bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. + { +// StringToBuffer f( filename ); +// return ( f.buffer && LoadFile( f.buffer, encoding )); + return LoadFile( filename.c_str(), encoding ); + } + bool SaveFile( const std::string& filename ) const ///< STL std::string version. + { +// StringToBuffer f( filename ); +// return ( f.buffer && SaveFile( f.buffer )); + return SaveFile( filename.c_str() ); + } + #endif + + /** Parse the given null terminated block of xml data. Passing in an encoding to this + method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml + to use that encoding, regardless of what TinyXml might otherwise try to detect. + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** Get the root element -- the only top level element -- of the document. + In well formed XML, there should only be one. TinyXml is tolerant of + multiple elements at the document level. + */ + const TiXmlElement* RootElement() const { return FirstChildElement(); } + TiXmlElement* RootElement() { return FirstChildElement(); } + + /** If an error occurs, Error will be set to true. Also, + - The ErrorId() will contain the integer identifier of the error (not generally useful) + - The ErrorDesc() method will return the name of the error. (very useful) + - The ErrorRow() and ErrorCol() will return the location of the error (if known) + */ + bool Error() const { return error; } + + /// Contains a textual (english) description of the error if one occurs. + const char * ErrorDesc() const { return errorDesc.c_str (); } + + /** Generally, you probably want the error string ( ErrorDesc() ). But if you + prefer the ErrorId, this function will fetch it. + */ + int ErrorId() const { return errorId; } + + /** Returns the location (if known) of the error. The first column is column 1, + and the first row is row 1. A value of 0 means the row and column wasn't applicable + (memory errors, for example, have no row/column) or the parser lost the error. (An + error in the error reporting, in that case.) + + @sa SetTabSize, Row, Column + */ + int ErrorRow() const { return errorLocation.row+1; } + int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() + + /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) + to report the correct values for row and column. It does not change the output + or input in any way. + + By calling this method, with a tab size + greater than 0, the row and column of each node and attribute is stored + when the file is loaded. Very useful for tracking the DOM back in to + the source file. + + The tab size is required for calculating the location of nodes. If not + set, the default of 4 is used. The tabsize is set per document. Setting + the tabsize to 0 disables row/column tracking. + + Note that row and column tracking is not supported when using operator>>. + + The tab size needs to be enabled before the parse or load. Correct usage: + @verbatim + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Load( "myfile.xml" ); + @endverbatim + + @sa Row, Column + */ + void SetTabSize( int _tabsize ) { tabsize = _tabsize; } + + int TabSize() const { return tabsize; } + + /** If you have handled the error, it can be reset with this call. The error + state is automatically cleared if you Parse a new XML block. + */ + void ClearError() { error = false; + errorId = 0; + errorDesc = ""; + errorLocation.row = errorLocation.col = 0; + //errorLocation.last = 0; + } + + /** Write the document to standard out using formatted printing ("pretty print"). */ + void Print() const { Print( stdout, 0 ); } + + /* Write the document to a string using formatted printing ("pretty print"). This + will allocate a character array (new char[]) and return it as a pointer. The + calling code pust call delete[] on the return char* to avoid a memory leak. + */ + //char* PrintToMemory() const; + + /// Print this Document to a FILE stream. + virtual void Print( FILE* cfile, int depth = 0 ) const; + // [internal use] + void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + + virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +#ifdef HAS_ICONV + void SetConvertToUtf8(bool convert) { convertToUtf8 = convert; } + bool convertToUtf8; + iconv_t iconvContext; +#endif + +protected : + // [internal use] + virtual TiXmlNode* Clone() const; + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + void CopyTo( TiXmlDocument* target ) const; + + bool error; + int errorId; + TIXML_STRING errorDesc; + int tabsize; + TiXmlCursor errorLocation; + bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write. +}; + + +/** + A TiXmlHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + <Document> + <Element attributeA = "valueA"> + <Child attributeB = "value1" /> + <Child attributeB = "value2" /> + </Element> + <Document> + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + TiXmlElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity + of such code. A TiXmlHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + TiXmlHandle docHandle( &document ); + TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + TiXmlHandle handleCopy = handle; + @endverbatim + + What they should not be used for is iteration: + + @verbatim + int i=0; + while ( true ) + { + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement(); + if ( !child ) + break; + // do something + ++i; + } + @endverbatim + + It seems reasonable, but it is in fact two embedded while loops. The Child method is + a linear walk to find the element, so this code would iterate much more than it needs + to. Instead, prefer: + + @verbatim + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement(); + + for( child; child; child=child->NextSiblingElement() ) + { + // do something + } + @endverbatim +*/ +class TiXmlHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + TiXmlHandle( TiXmlNode* _node ) { this->node = _node; } + /// Copy constructor + TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } + TiXmlHandle operator=( const TiXmlHandle& ref ) { this->node = ref.node; return *this; } + + /// Return a handle to the first child node. + TiXmlHandle FirstChild() const; + /// Return a handle to the first child node with the given name. + TiXmlHandle FirstChild( const char * value ) const; + /// Return a handle to the first child element. + TiXmlHandle FirstChildElement() const; + /// Return a handle to the first child element with the given name. + TiXmlHandle FirstChildElement( const char * value ) const; + + /** Return a handle to the "index" child with the given name. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( const char* value, int index ) const; + /** Return a handle to the "index" child. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( int index ) const; + /** Return a handle to the "index" child element with the given name. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( const char* value, int index ) const; + /** Return a handle to the "index" child element. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( int index ) const; + + #ifdef TIXML_USE_STL + TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } + TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } + + TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } + TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } + #endif + + /** Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* ToNode() const { return node; } + /** Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } + /** Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } + /** Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } + + /** @deprecated use ToNode. + Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* Node() const { return ToNode(); } + /** @deprecated use ToElement. + Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* Element() const { return ToElement(); } + /** @deprecated use ToText() + Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* Text() const { return ToText(); } + /** @deprecated use ToUnknown() + Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* Unknown() const { return ToUnknown(); } + +private: + TiXmlNode* node; +}; + + +/** Print to memory functionality. The TiXmlPrinter is useful when you need to: + + -# Print to memory (especially in non-STL mode) + -# Control formatting (line endings, etc.) + + When constructed, the TiXmlPrinter is in its default "pretty printing" mode. + Before calling Accept() you can call methods to control the printing + of the XML document. After TiXmlNode::Accept() is called, the printed document can + be accessed via the CStr(), Str(), and Size() methods. + + TiXmlPrinter uses the Visitor API. + @verbatim + TiXmlPrinter printer; + printer.SetIndent( "\t" ); + + doc.Accept( &printer ); + fprintf( stdout, "%s", printer.CStr() ); + @endverbatim +*/ +class TiXmlPrinter : public TiXmlVisitor +{ +public: + TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ), + buffer(), indent( " " ), lineBreak( "\n" ) {} + + virtual bool VisitEnter( const TiXmlDocument& doc ); + virtual bool VisitExit( const TiXmlDocument& doc ); + + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + virtual bool VisitExit( const TiXmlElement& element ); + + virtual bool Visit( const TiXmlDeclaration& declaration ); + virtual bool Visit( const TiXmlText& text ); + virtual bool Visit( const TiXmlComment& comment ); + virtual bool Visit( const TiXmlUnknown& unknown ); + + /** Set the indent characters for printing. By default 4 spaces + but tab (\t) is also useful, or null/empty string for no indentation. + */ + void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; } + /// Query the indention string. + const char* Indent() { return indent.c_str(); } + /** Set the line breaking string. By default set to newline (\n). + Some operating systems prefer other characters, or can be + set to the null/empty string for no indenation. + */ + void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; } + /// Query the current line breaking string. + const char* LineBreak() { return lineBreak.c_str(); } + + /** Switch over to "stream printing" which is the most dense formatting without + linebreaks. Common when the XML is needed for network transmission. + */ + void SetStreamPrinting() { indent = ""; + lineBreak = ""; + } + /// Return the result. + const char* CStr() { return buffer.c_str(); } + /// Return the length of the result string. + size_t Size() { return buffer.size(); } + + #ifdef TIXML_USE_STL + /// Return the result. + const std::string& Str() { return buffer; } + #endif + +private: + void DoIndent() { + for( int i=0; i<depth; ++i ) + buffer += indent; + } + void DoLineBreak() { + buffer += lineBreak; + } + + int depth; + bool simpleTextPrint; + TIXML_STRING buffer; + TIXML_STRING indent; + TIXML_STRING lineBreak; +}; + + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif + diff --git a/guilib/tinyXML/tinyxmlerror.cpp b/guilib/tinyXML/tinyxmlerror.cpp new file mode 100644 index 0000000000..d24f63b2e5 --- /dev/null +++ b/guilib/tinyXML/tinyxmlerror.cpp @@ -0,0 +1,53 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +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. +*/ + +#include "tinyxml.h" + +// The goal of the seperate error file is to make the first +// step towards localization. tinyxml (currently) only supports +// english error messages, but the could now be translated. +// +// It also cleans up the code a bit. +// + +const char* TiXmlBase::errorString[ TIXML_ERROR_STRING_COUNT ] = +{ + "No error", + "Error", + "Failed to open file", + "Memory allocation failed.", + "Error parsing Element.", + "Failed to read Element name", + "Error reading Element value.", + "Error reading Attributes.", + "Error: empty tag.", + "Error reading end tag.", + "Error parsing Unknown.", + "Error parsing Comment.", + "Error parsing Declaration.", + "Error document empty.", + "Error null (0) or unexpected EOF found in input stream.", + "Error parsing CDATA.", + "Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.", +}; diff --git a/guilib/tinyXML/tinyxmlparser.cpp b/guilib/tinyXML/tinyxmlparser.cpp new file mode 100644 index 0000000000..712eb10654 --- /dev/null +++ b/guilib/tinyXML/tinyxmlparser.cpp @@ -0,0 +1,1683 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) + +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. +*/ + +#include <ctype.h> +#include <stddef.h> + +#include "tinyxml.h" +#include "CharsetConverter.h" + +//#define DEBUG_PARSER +#if defined( DEBUG_PARSER ) +# if defined( DEBUG ) && defined( _MSC_VER ) +# include <windows.h> +# define TIXML_LOG OutputDebugString +# else +# define TIXML_LOG printf +# endif +#endif + +// Note tha "PutString" hardcodes the same list. This +// is less flexible than it appears. Changing the entries +// or order will break putstring. +TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] = +{ + { "&", 5, '&' }, + { "<", 4, '<' }, + { ">", 4, '>' }, + { """, 6, '\"' }, + { "'", 6, '\'' } +}; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// Including the basic of this table, which determines the #bytes in the +// sequence from the lead byte. 1 placed for invalid sequences -- +// although the result will be junk, pass it through as much as possible. +// Beware of the non-characters in UTF-8: +// ef bb bf (Microsoft "lead bytes") +// ef bf be +// ef bf bf + +const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +const int TiXmlBase::utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; + + +void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) + *length = 1; + else if ( input < 0x800 ) + *length = 2; + else if ( input < 0x10000 ) + *length = 3; + else if ( input < 0x200000 ) + *length = 4; + else + { *length = 0; return; } // This code won't covert this correctly anyway. + + output += *length; + + // Scary scary fall throughs. + switch (*length) + { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + } +} + + +/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalpha( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalpha( anyByte ); +// } +} + + +/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalnum( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalnum( anyByte ); +// } +} + + +class TiXmlParsingData +{ + friend class TiXmlDocument; + public: + void Stamp( const char* now, TiXmlEncoding encoding ); + + const TiXmlCursor& Cursor() { return cursor; } + + private: + // Only used by the document! + TiXmlParsingData( const char* start, int _tabsize, int row, int col ) + { + assert( start ); + stamp = start; + tabsize = _tabsize; + cursor.row = row; + cursor.col = col; + } + + TiXmlCursor cursor; + const char* stamp; + int tabsize; +}; + + +void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) +{ + assert( now ); + + // Do nothing if the tabsize is 0. + if ( tabsize < 1 ) + { + return; + } + + // Get the current row, column. + int row = cursor.row; + int col = cursor.col; + const char* p = stamp; + assert( p ); + + while ( p < now ) + { + // Treat p as unsigned, so we have a happy compiler. + const unsigned char* pU = (const unsigned char*)p; + + // Code contributed by Fletcher Dunn: (modified by lee) + switch (*pU) { + case 0: + // We *should* never get here, but in case we do, don't + // advance past the terminating null character, ever + return; + + case '\r': + // bump down to the next line + ++row; + col = 0; + // Eat the character + ++p; + + // Check for \r\n sequence, and treat this as a single character + if (*p == '\n') { + ++p; + } + break; + + case '\n': + // bump down to the next line + ++row; + col = 0; + + // Eat the character + ++p; + + // Check for \n\r sequence, and treat this as a single + // character. (Yes, this bizarre thing does occur still + // on some arcane platforms...) + if (*p == '\r') { + ++p; + } + break; + + case '\t': + // Eat the character + ++p; + + // Skip to next tab stop + col = (col / tabsize + 1) * tabsize; + break; + + case TIXML_UTF_LEAD_0: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( *(p+1) && *(p+2) ) + { + // In these cases, don't advance the column. These are + // 0-width spaces. + if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) + p += 3; + else + { p +=3; ++col; } // A normal character. + } + } + else + { + ++p; + ++col; + } + break; + + default: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // Eat the 1 to 4 byte utf8 character. + int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; + if ( step == 0 ) + step = 1; // Error case from bad encoding, but handle gracefully. + p += step; + + // Just advance one column, of course. + ++col; + } + else + { + ++p; + ++col; + } + break; + } + } + cursor.row = row; + cursor.col = col; + assert( cursor.row >= -1 ); + assert( cursor.col >= -1 ); + stamp = p; + assert( stamp ); +} + + +const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) +{ + if ( !p || !*p ) + { + return 0; + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + while ( *p ) + { + const unsigned char* pU = (const unsigned char*)p; + + // Skip the stupid Microsoft UTF-8 Byte order marks + if ( *(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==TIXML_UTF_LEAD_1 + && *(pU+2)==TIXML_UTF_LEAD_2 ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbeU ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbfU ) + { + p += 3; + continue; + } + + if ( IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) // Still using old rules for white space. + ++p; + else + break; + } + } + else + { + while ( *p && (IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' )) + ++p; + } + + return p; +} + +#ifdef TIXML_USE_STL +/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) +{ + for( ;; ) + { + if ( !in->good() ) return false; + + int c = in->peek(); + // At this scope, we can't get to a document. So fail silently. + if ( !IsWhiteSpace( c ) || c <= 0 ) + return true; + + *tag += (char) in->get(); + } +} + +/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) +{ + //assert( character > 0 && character < 128 ); // else it won't work in utf-8 + while ( in->good() ) + { + int c = in->peek(); + if ( c == character ) + return true; + if ( c <= 0 ) // Silent failure: can't get document at this scope + return false; + + in->get(); + *tag += (char) c; + } + return false; +} +#endif + +// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The +// "assign" optimization removes over 10% of the execution time. +// +const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) +{ + // Oddly, not supported on some comilers, + //name->clear(); + // So use this: + *name = ""; + assert( p ); + + // Names start with letters or underscores. + // Of course, in unicode, tinyxml has no idea what a letter *is*. The + // algorithm is generous. + // + // After that, they can be letters, underscores, numbers, + // hyphens, or colons. (Colons are valid ony for namespaces, + // but tinyxml can't tell namespaces from names.) + if ( p && *p + && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) + { + const char* start = p; + while( p && *p + && ( IsAlphaNum( (unsigned char ) *p, encoding ) + || *p == '_' + || *p == '-' + || *p == '.' + || *p == ':' ) ) + { + //(*name) += *p; // expensive + ++p; + } + if ( p-start > 0 ) { + name->assign( start, p-start ); + } + return p; + } + return 0; +} + +const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) +{ + // Presume an entity, and pull it out. + TIXML_STRING ent; + int i; + + if ( *(p+1) && *(p+1) == '#' && *(p+2) ) + { + unsigned long ucs = 0; + ptrdiff_t delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) + { + // Hexadecimal. + if ( !*(p+3) ) return 0; + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != 'x' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else if ( *q >= 'a' && *q <= 'f' ) + ucs += mult * (*q - 'a' + 10); + else if ( *q >= 'A' && *q <= 'F' ) + ucs += mult * (*q - 'A' + 10 ); + else + return 0; + mult *= 16; + --q; + } + } + else + { + // Decimal. + if ( !*(p+2) ) return 0; + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != '#' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else + return 0; + mult *= 10; + --q; + } + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + } + else + { + *value = (char)ucs; + *length = 1; + } + return p + delta + 1; + } + + // Now try to match it. + for( i=0; i<NUM_ENTITY; ++i ) + { + if ( strncmp( entity[i].str, p, entity[i].strLength ) == 0 ) + { + assert( strlen( entity[i].str ) == entity[i].strLength ); + *value = entity[i].chr; + *length = 1; + return ( p + entity[i].strLength ); + } + } + + // So it wasn't an entity, its unrecognized, or something like that. + *value = *p; // Don't put back the last one, since we return it! + *length = 1; // Leave unrecognized entities - this doesn't really work. + // Just writes strange XML. + return p+1; +} + + +bool TiXmlBase::StringEqual( const char* p, + const char* tag, + bool ignoreCase, + TiXmlEncoding encoding ) +{ + assert( p ); + assert( tag ); + if ( !p || !*p ) + { + assert( 0 ); + return false; + } + + const char* q = p; + + if ( ignoreCase ) + { + while ( *q && *tag && ToLower( *q, encoding ) == ToLower( *tag, encoding ) ) + { + ++q; + ++tag; + } + + if ( *tag == 0 ) + return true; + } + else + { + while ( *q && *tag && *q == *tag ) + { + ++q; + ++tag; + } + + if ( *tag == 0 ) // Have we found the end of the tag, and everything equal? + return true; + } + return false; +} + +const char* TiXmlBase::ReadText( const char* p, + TIXML_STRING * text, + bool trimWhiteSpace, + const char* endTag, + bool caseInsensitive, + TiXmlEncoding encoding ) +{ + *text = ""; + if ( !trimWhiteSpace // certain tags always keep whitespace + || !condenseWhiteSpace ) // if true, whitespace is always kept + { + // Keep all the white space. + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) + ) + { + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + text->append( cArr, len ); + } + } + else + { + bool whitespace = false; + + // Remove leading white space: + p = SkipWhiteSpace( p, encoding ); + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) ) + { + if ( *p == '\r' || *p == '\n' ) + { + whitespace = true; + ++p; + } + else if ( IsWhiteSpace( *p ) ) + { + whitespace = true; + ++p; + } + else + { + // If we've found whitespace, add it before the + // new character. Any whitespace just becomes a space. + if ( whitespace ) + { + (*text) += ' '; + whitespace = false; + } + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + if ( len == 1 ) + (*text) += cArr[0]; // more efficient + else + text->append( cArr, len ); + } + } + } + if ( p ) + p += strlen( endTag ); + return p; +} + +#ifdef HAS_ICONV +void TiXmlBase::ConvertToUtf8(TiXmlDocument* document, TIXML_STRING* text) +{ + if (!document) return; + if (!document->convertToUtf8) return; + if (document->iconvContext == (iconv_t) -1) return; + +#ifdef TIXML_USE_STL + + size_t olen = (text->size() * 4) + 1; + char* output = new char[olen]; + char* obuf = output; + size_t ilen = text->size() + 1; + const char* ibuf = (const char*) text->c_str(); + + if (iconv_const(document->iconvContext, &ibuf, &ilen, &obuf, &olen) == (size_t) -1) + { + delete [] output; + return; + } + + *text = output; + delete [] output; +#endif +} +#endif + +#ifdef TIXML_USE_STL + +void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + // The basic issue with a document is that we don't know what we're + // streaming. Read something presumed to be a tag (and hope), then + // identify it, and call the appropriate stream method on the tag. + // + // This "pre-streaming" will never read the closing ">" so the + // sub-tag can orient itself. + + if ( !StreamTo( in, '<', tag ) ) + { + SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + while ( in->good() ) + { + int tagIndex = (int) tag->length(); + while ( in->good() && in->peek() != '>' ) + { + int c = in->get(); + if ( c <= 0 ) + { + SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + break; + } + (*tag) += (char) c; + } + + if ( in->good() ) + { + // We now have something we presume to be a node of + // some sort. Identify it, and call the node to + // continue streaming. + TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); + + if ( node ) + { + node->StreamIn( in, tag ); + bool isElement = node->ToElement() != 0; + delete node; + node = 0; + + // If this is the root element, we're done. Parsing will be + // done by the >> operator. + if ( isElement ) + { + return; + } + } + else + { + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + } + } + // We should have returned sooner. + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); +} + +#endif + +const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) +{ + ClearError(); + + // Parse away, at the document level. Since a document + // contains nothing but other tags, most of what happens + // here is skipping white space. + if ( !p || !*p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + // Note that, for a document, this needs to come + // before the while space skip, so that parsing + // starts from the pointer we are given. + location.Clear(); + if ( prevData ) + { + location.row = prevData->cursor.row; + location.col = prevData->cursor.col; + } + else + { + location.row = 0; + location.col = 0; + } + TiXmlParsingData data( p, TabSize(), location.row, location.col ); + location = data.Cursor(); + + if ( encoding == TIXML_ENCODING_UNKNOWN ) + { + // Check for the Microsoft UTF-8 lead bytes. + const unsigned char* pU = (const unsigned char*)p; + if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 + && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 + && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) + { + encoding = TIXML_ENCODING_UTF8; + useMicrosoftBOM = true; + } + } + + p = SkipWhiteSpace( p, encoding ); + if ( !p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + while ( p && *p ) + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, &data, encoding ); + LinkEndChild( node ); + } + else + { + break; + } + + // Did we get encoding info? + if ( encoding == TIXML_ENCODING_UNKNOWN + && node->ToDeclaration() ) + { + TiXmlDeclaration* dec = node->ToDeclaration(); + const char* enc = dec->Encoding(); + assert( enc ); + + if ( *enc == 0 ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice + else + { +#ifdef HAS_ICONV + if (convertToUtf8) + { + if (iconvContext != (iconv_t) -1) + { + iconv_close(iconvContext); + iconvContext = (iconv_t) -1; + } + + iconvContext = iconv_open("UTF8", enc); + } +#endif + encoding = TIXML_ENCODING_LEGACY; + } + } + + p = SkipWhiteSpace( p, encoding ); + } + + // Was this empty? + if ( !firstChild ) { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); + return 0; + } + + // All is well. + return p; +} + +void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + // The first error in a chain is more accurate - don't set again! + if ( error ) + return; + + assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); + error = true; + errorId = err; + errorDesc = errorString[ errorId ]; + + errorLocation.Clear(); + if ( pError && data ) + { + data->Stamp( pError, encoding ); + errorLocation = data->Cursor(); + } +} + + +TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) +{ + TiXmlNode* returnNode = 0; + + p = SkipWhiteSpace( p, encoding ); + if( !p || !*p || *p != '<' ) + { + return 0; + } + + TiXmlDocument* doc = GetDocument(); + p = SkipWhiteSpace( p, encoding ); + + if ( !p || !*p ) + { + return 0; + } + + // What is this thing? + // - Elements start with a letter or underscore, but xml is reserved. + // - Comments: <!-- + // - Decleration: <?xml + // - Everthing else is unknown to tinyxml. + // + + const char* xmlHeader = { "<?xml" }; + const char* commentHeader = { "<!--" }; + const char* dtdHeader = { "<!" }; + const char* cdataHeader = { "<![CDATA[" }; + + if ( StringEqual( p, xmlHeader, true, encoding ) ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Declaration\n" ); + #endif + returnNode = new TiXmlDeclaration(); + } + else if ( StringEqual( p, commentHeader, false, encoding ) ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Comment\n" ); + #endif + returnNode = new TiXmlComment(); + } + else if ( StringEqual( p, cdataHeader, false, encoding ) ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing CDATA\n" ); + #endif + TiXmlText* text = new TiXmlText( "" ); + text->SetCDATA( true ); + returnNode = text; + } + else if ( StringEqual( p, dtdHeader, false, encoding ) ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Unknown(1)\n" ); + #endif + returnNode = new TiXmlUnknown(); + } + else if ( IsAlpha( *(p+1), encoding ) + || *(p+1) == '_' ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Element\n" ); + #endif + returnNode = new TiXmlElement( "" ); + } + else + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Unknown(2)\n" ); + #endif + returnNode = new TiXmlUnknown(); + } + + if ( returnNode ) + { + // Set the parent, so it can report errors + returnNode->parent = this; + } + else + { + if ( doc ) + doc->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } + return returnNode; +} + +#ifdef TIXML_USE_STL + +void TiXmlElement::StreamIn (std::istream * in, TIXML_STRING * tag) +{ + // We're called with some amount of pre-parsing. That is, some of "this" + // element is in "tag". Go ahead and stream to the closing ">" + while( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c ; + + if ( c == '>' ) + break; + } + + if ( tag->length() < 3 ) return; + + // Okay...if we are a "/>" tag, then we're done. We've read a complete tag. + // If not, identify and stream. + + if ( tag->at( tag->length() - 1 ) == '>' + && tag->at( tag->length() - 2 ) == '/' ) + { + // All good! + return; + } + else if ( tag->at( tag->length() - 1 ) == '>' ) + { + // There is more. Could be: + // text + // cdata text (which looks like another node) + // closing tag + // another node. + for ( ;; ) + { + StreamWhiteSpace( in, tag ); + + // Do we have text? + if ( in->good() && in->peek() != '<' ) + { + // Yep, text. + TiXmlText text( "" ); + text.StreamIn( in, tag ); + + // What follows text is a closing tag or another node. + // Go around again and figure it out. + continue; + } + + // We now have either a closing tag...or another node. + // We should be at a "<", regardless. + if ( !in->good() ) return; + assert( in->peek() == '<' ); + int tagIndex = (int) tag->length(); + + bool closingTag = false; + bool firstCharFound = false; + + for( ;; ) + { + if ( !in->good() ) + return; + + int c = in->peek(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + if ( c == '>' ) + break; + + *tag += (char) c; + in->get(); + + // Early out if we find the CDATA id. + if ( c == '[' && tag->size() >= 9 ) + { + size_t len = tag->size(); + const char* start = tag->c_str() + len - 9; + if ( strcmp( start, "<![CDATA[" ) == 0 ) { + assert( !closingTag ); + break; + } + } + + if ( !firstCharFound && c != '<' && !IsWhiteSpace( c ) ) + { + firstCharFound = true; + if ( c == '/' ) + closingTag = true; + } + } + // If it was a closing tag, then read in the closing '>' to clean up the input stream. + // If it was not, the streaming will be done by the tag. + if ( closingTag ) + { + if ( !in->good() ) + return; + + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + assert( c == '>' ); + *tag += (char) c; + + // We are done, once we've found our closing tag. + return; + } + else + { + // If not a closing tag, id it, and stream. + const char* tagloc = tag->c_str() + tagIndex; + TiXmlNode* node = Identify( tagloc, TIXML_DEFAULT_ENCODING ); + if ( !node ) + return; + node->StreamIn( in, tag ); + delete node; + node = 0; + + // No return: go around from the beginning: text, closing tag, or node. + } + } + } +} +#endif + +const char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + TiXmlDocument* document = GetDocument(); + + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding ); + return 0; + } + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + if ( *p != '<' ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding ); + return 0; + } + + p = SkipWhiteSpace( p+1, encoding ); + + // Read the name. + const char* pErr = p; + + p = ReadName( p, &value, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding ); + return 0; + } + + TIXML_STRING endTag ("</"); + endTag += value; + endTag += ">"; + + // Check for and read attributes. Also look for an empty + // tag or an end tag. + while ( p && *p ) + { + pErr = p; + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + if ( *p == '/' ) + { + ++p; + // Empty tag. + if ( *p != '>' ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding ); + return 0; + } + return (p+1); + } + else if ( *p == '>' ) + { + // Done with attributes (if there were any.) + // Read the value -- which can include other + // elements -- read the end tag, and return. + ++p; + p = ReadValue( p, data, encoding ); // Note this is an Element method, and will set the error if one happens. + if ( !p || !*p ) { + // We were looking for the end tag, but found nothing. + // Fix for [ 1663758 ] Failure to report error on bad XML + if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding ); + return 0; + } + + // We should find the end tag now + if ( StringEqual( p, endTag.c_str(), false, encoding ) ) + { + p += endTag.length(); + return p; + } + else + { + if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding ); + return 0; + } + } + else + { + // Try to read an attribute: + TiXmlAttribute* attrib = new TiXmlAttribute(); + if ( !attrib ) + { + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, pErr, data, encoding ); + return 0; + } + + attrib->SetDocument( document ); + pErr = p; + p = attrib->Parse( p, data, encoding ); + + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding ); + delete attrib; + return 0; + } + + // Handle the strange case of double attributes: + #ifdef TIXML_USE_STL + TiXmlAttribute* node = attributeSet.Find( attrib->NameTStr() ); + #else + TiXmlAttribute* node = attributeSet.Find( attrib->Name() ); + #endif + if ( node ) + { + node->SetValue( attrib->Value() ); + delete attrib; + return 0; + } + + attributeSet.Add( attrib ); + } + } + return p; +} + + +const char* TiXmlElement::ReadValue( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + TiXmlDocument* document = GetDocument(); + // Read in text and elements in any order. + const char* pWithWhiteSpace = p; + p = SkipWhiteSpace( p, encoding ); + + while ( p && *p ) + { + if ( *p != '<' ) + { + // Take what we have, make a text element. + TiXmlText* textNode = new TiXmlText( "" ); + + if ( !textNode ) + { + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, encoding ); + return 0; + } + + if ( TiXmlBase::IsWhiteSpaceCondensed() ) + { + p = textNode->Parse( p, data, encoding ); + } + else + { + // Special case: we want to keep the white space + // so that leading spaces aren't removed. + p = textNode->Parse( pWithWhiteSpace, data, encoding ); + } + + if ( !textNode->Blank() ) + { + LinkEndChild( textNode ); +#ifdef HAS_ICONV + ConvertToUtf8(document, &textNode->value); +#endif + } + else + delete textNode; + } + else + { + // We hit a '<' + // Have we hit a new element or an end tag? This could also be + // a TiXmlText in the "CDATA" style. + if ( StringEqual( p, "</", false, encoding ) ) + { + return p; + } + else + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, data, encoding ); + LinkEndChild( node ); + } + else + { + return 0; + } + } + } + pWithWhiteSpace = p; + p = SkipWhiteSpace( p, encoding ); + } + + if ( !p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ELEMENT_VALUE, 0, 0, encoding ); + } + return p; +} + + +#ifdef TIXML_USE_STL +void TiXmlUnknown::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + + +const char* TiXmlUnknown::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + TiXmlDocument* document = GetDocument(); + p = SkipWhiteSpace( p, encoding ); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + if ( !p || !*p || *p != '<' ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, p, data, encoding ); + return 0; + } + ++p; + value = ""; + + while ( p && *p && *p != '>' ) + { + value += *p; + ++p; + } + + if ( !p ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding ); + } + if ( *p == '>' ) + return p+1; + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlComment::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + + if ( c == '>' + && tag->at( tag->length() - 2 ) == '-' + && tag->at( tag->length() - 3 ) == '-' ) + { + // All is well. + return; + } + } +} +#endif + + +const char* TiXmlComment::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + TiXmlDocument* document = GetDocument(); + value = ""; + + p = SkipWhiteSpace( p, encoding ); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + const char* startTag = "<!--"; + const char* endTag = "-->"; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // [ 1475201 ] TinyXML parses entities in comments + // Oops - ReadText doesn't work, because we don't want to parse the entities. + // p = ReadText( p, &value, false, endTag, false, encoding ); + // + // from the XML spec: + /* + [Definition: Comments may appear anywhere in a document outside other markup; in addition, + they may appear within the document type declaration at places allowed by the grammar. + They are not part of the document's character data; an XML processor MAY, but need not, + make it possible for an application to retrieve the text of comments. For compatibility, + the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity + references MUST NOT be recognized within comments. + + An example of a comment: + + <!-- declarations for <head> & <body> --> + */ + + value = ""; + // Keep all the white space. + while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) + { + value.append( p, 1 ); + ++p; + } + if ( p ) + p += strlen( endTag ); + + return p; +} + + +const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) return 0; + +// int tabsize = 4; +// if ( document ) +// tabsize = document->TabSize(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + // Read the name, the '=' and the value. + const char* pErr = p; + p = ReadName( p, &name, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p || *p != '=' ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + ++p; // skip '=' + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + const char* end; + const char SINGLE_QUOTE = '\''; + const char DOUBLE_QUOTE = '\"'; + + if ( *p == SINGLE_QUOTE ) + { + ++p; + end = "\'"; // single quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else if ( *p == DOUBLE_QUOTE ) + { + ++p; + end = "\""; // double quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else + { + // All attribute values should be in single or double quotes. + // But this is such a common error that the parser will try + // its best, even without them. + value = ""; + while ( p && *p // existence + && !IsWhiteSpace( *p ) && *p != '\n' && *p != '\r' // whitespace + && *p != '/' && *p != '>' ) // tag end + { + if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { + // [ 1451649 ] Attribute values with trailing quotes not handled correctly + // We did not have an opening quote but seem to have a + // closing one. Give up and throw an error. + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + value += *p; + ++p; + } + } + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->peek(); + if ( !cdata && (c == '<' ) ) + { + return; + } + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + in->get(); // "commits" the peek made above + + if ( cdata && c == '>' && tag->size() >= 3 ) { + size_t len = tag->size(); + if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { + // terminator of cdata. + return; + } + } + } +} +#endif + +const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + value = ""; + TiXmlDocument* document = GetDocument(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + const char* const startTag = "<![CDATA["; + const char* const endTag = "]]>"; + + if ( cdata || StringEqual( p, startTag, false, encoding ) ) + { + cdata = true; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // Keep all the white space, ignore the encoding, etc. + while ( p && *p + && !StringEqual( p, endTag, false, encoding ) + ) + { + value += *p; + ++p; + } + + TIXML_STRING dummy; + p = ReadText( p, &dummy, false, endTag, false, encoding ); + return p; + } + else + { + bool ignoreWhite = true; + + const char* end = "<"; + p = ReadText( p, &value, ignoreWhite, end, false, encoding ); + if ( p ) + return p-1; // don't truncate the '<' + return 0; + } +} + +#ifdef TIXML_USE_STL +void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + +const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) +{ + p = SkipWhiteSpace( p, _encoding ); + // Find the beginning, find the end, and look for + // the stuff in-between. + TiXmlDocument* document = GetDocument(); + if ( !p || !*p || !StringEqual( p, "<?xml", true, _encoding ) ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); + return 0; + } + if ( data ) + { + data->Stamp( p, _encoding ); + location = data->Cursor(); + } + p += 5; + + version = ""; + encoding = ""; + standalone = ""; + + while ( p && *p ) + { + if ( *p == '>' ) + { + ++p; + return p; + } + + p = SkipWhiteSpace( p, _encoding ); + if ( StringEqual( p, "version", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + version = attrib.Value(); + } + else if ( StringEqual( p, "encoding", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + encoding = attrib.Value(); + } + else if ( StringEqual( p, "standalone", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + standalone = attrib.Value(); + } + else + { + // Read over whatever it is. + while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) + ++p; + } + } + return 0; +} + +bool TiXmlText::Blank() const +{ + for ( unsigned i=0; i<value.length(); i++ ) + if ( !IsWhiteSpace( value[i] ) ) + return false; + return true; +} + |