mirror of
https://github.com/TTimo/GtkRadiant.git
synced 2025-01-09 11:30:51 +00:00
import urban terror bumpy q3map2 from http://code.google.com/p/urt-bumpy-q3map2/ - removed some trash files, no modifications yet
This commit is contained in:
parent
c22d17a379
commit
31286fb558
507 changed files with 162910 additions and 0 deletions
1
tools/urt/libs/.cvsignore
Normal file
1
tools/urt/libs/.cvsignore
Normal file
|
@ -0,0 +1 @@
|
|||
.consign
|
2
tools/urt/libs/archivelib.cpp
Normal file
2
tools/urt/libs/archivelib.cpp
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include "archivelib.h"
|
237
tools/urt/libs/archivelib.h
Normal file
237
tools/urt/libs/archivelib.h
Normal file
|
@ -0,0 +1,237 @@
|
|||
|
||||
#if !defined (INCLUDED_ARCHIVELIB_H)
|
||||
#define INCLUDED_ARCHIVELIB_H
|
||||
|
||||
#include "debugging/debugging.h"
|
||||
#include "iarchive.h"
|
||||
#include "stream/filestream.h"
|
||||
#include "stream/textfilestream.h"
|
||||
#include "memory/allocator.h"
|
||||
#include "string/string.h"
|
||||
|
||||
/// \brief A single-byte-reader wrapper around an InputStream.
|
||||
/// Optimised for reading one byte at a time.
|
||||
/// Uses a buffer to reduce the number of times the wrapped stream must be read.
|
||||
template<typename InputStreamType, int SIZE = 1024>
|
||||
class SingleByteInputStream
|
||||
{
|
||||
typedef typename InputStreamType::byte_type byte_type;
|
||||
enum { c_bufferSize = SIZE };
|
||||
|
||||
InputStreamType& m_inputStream;
|
||||
byte_type m_buffer[c_bufferSize];
|
||||
byte_type* m_cur;
|
||||
byte_type* m_end;
|
||||
|
||||
public:
|
||||
|
||||
SingleByteInputStream(InputStreamType& inputStream) : m_inputStream(inputStream), m_cur(m_buffer + c_bufferSize), m_end(m_cur)
|
||||
{
|
||||
}
|
||||
bool readByte(byte_type& b)
|
||||
{
|
||||
if(m_cur == m_end)
|
||||
{
|
||||
if(m_end != m_buffer + c_bufferSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_end = m_buffer + m_inputStream.read(m_buffer, c_bufferSize);
|
||||
m_cur = m_buffer;
|
||||
|
||||
if(m_end == m_buffer)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
b = *m_cur++;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief A binary-to-text wrapper around an InputStream.
|
||||
/// Converts CRLF or LFCR line-endings to LF line-endings.
|
||||
template<typename BinaryInputStreamType>
|
||||
class BinaryToTextInputStream : public TextInputStream
|
||||
{
|
||||
SingleByteInputStream<BinaryInputStreamType> m_inputStream;
|
||||
public:
|
||||
BinaryToTextInputStream(BinaryInputStreamType& inputStream) : m_inputStream(inputStream)
|
||||
{
|
||||
}
|
||||
std::size_t read(char* buffer, std::size_t length)
|
||||
{
|
||||
char* p = buffer;
|
||||
for(;;)
|
||||
{
|
||||
if(length != 0 && m_inputStream.readByte(*reinterpret_cast<typename BinaryInputStreamType::byte_type*>(p)))
|
||||
{
|
||||
if(*p != '\r')
|
||||
{
|
||||
++p;
|
||||
--length;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return p - buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief An ArchiveFile which is stored uncompressed as part of a larger archive file.
|
||||
class StoredArchiveFile : public ArchiveFile
|
||||
{
|
||||
CopiedString m_name;
|
||||
FileInputStream m_filestream;
|
||||
SubFileInputStream m_substream;
|
||||
FileInputStream::size_type m_size;
|
||||
public:
|
||||
typedef FileInputStream::size_type size_type;
|
||||
typedef FileInputStream::position_type position_type;
|
||||
|
||||
StoredArchiveFile(const char* name, const char* archiveName, position_type position, size_type stream_size, size_type file_size)
|
||||
: m_name(name), m_filestream(archiveName), m_substream(m_filestream, position, stream_size), m_size(file_size)
|
||||
{
|
||||
}
|
||||
|
||||
static StoredArchiveFile* create(const char* name, const char* archiveName, position_type position, size_type stream_size, size_type file_size)
|
||||
{
|
||||
return New<StoredArchiveFile>().scalar(name, archiveName, position, stream_size, file_size);
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
Delete<StoredArchiveFile>().scalar(this);
|
||||
}
|
||||
size_type size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
const char* getName() const
|
||||
{
|
||||
return m_name.c_str();
|
||||
}
|
||||
InputStream& getInputStream()
|
||||
{
|
||||
return m_substream;
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief An ArchiveTextFile which is stored uncompressed as part of a larger archive file.
|
||||
class StoredArchiveTextFile : public ArchiveTextFile
|
||||
{
|
||||
CopiedString m_name;
|
||||
FileInputStream m_filestream;
|
||||
SubFileInputStream m_substream;
|
||||
BinaryToTextInputStream<SubFileInputStream> m_textStream;
|
||||
public:
|
||||
typedef FileInputStream::size_type size_type;
|
||||
typedef FileInputStream::position_type position_type;
|
||||
|
||||
StoredArchiveTextFile(const char* name, const char* archiveName, position_type position, size_type stream_size)
|
||||
: m_name(name), m_filestream(archiveName), m_substream(m_filestream, position, stream_size), m_textStream(m_substream)
|
||||
{
|
||||
}
|
||||
|
||||
static StoredArchiveTextFile* create(const char* name, const char* archiveName, position_type position, size_type stream_size)
|
||||
{
|
||||
return New<StoredArchiveTextFile>().scalar(name, archiveName, position, stream_size);
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
Delete<StoredArchiveTextFile>().scalar(this);
|
||||
}
|
||||
const char* getName() const
|
||||
{
|
||||
return m_name.c_str();
|
||||
}
|
||||
TextInputStream& getInputStream()
|
||||
{
|
||||
return m_textStream;
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief An ArchiveFile which is stored as a single file on disk.
|
||||
class DirectoryArchiveFile : public ArchiveFile
|
||||
{
|
||||
CopiedString m_name;
|
||||
FileInputStream m_istream;
|
||||
FileInputStream::size_type m_size;
|
||||
public:
|
||||
typedef FileInputStream::size_type size_type;
|
||||
|
||||
DirectoryArchiveFile(const char* name, const char* filename)
|
||||
: m_name(name), m_istream(filename)
|
||||
{
|
||||
if(!failed())
|
||||
{
|
||||
m_istream.seek(0, FileInputStream::end);
|
||||
m_size = m_istream.tell();
|
||||
m_istream.seek(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_size = 0;
|
||||
}
|
||||
}
|
||||
bool failed() const
|
||||
{
|
||||
return m_istream.failed();
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
size_type size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
const char* getName() const
|
||||
{
|
||||
return m_name.c_str();
|
||||
}
|
||||
InputStream& getInputStream()
|
||||
{
|
||||
return m_istream;
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief An ArchiveTextFile which is stored as a single file on disk.
|
||||
class DirectoryArchiveTextFile : public ArchiveTextFile
|
||||
{
|
||||
CopiedString m_name;
|
||||
TextFileInputStream m_inputStream;
|
||||
public:
|
||||
|
||||
DirectoryArchiveTextFile(const char* name, const char* filename)
|
||||
: m_name(name), m_inputStream(filename)
|
||||
{
|
||||
}
|
||||
bool failed() const
|
||||
{
|
||||
return m_inputStream.failed();
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
const char* getName() const
|
||||
{
|
||||
return m_name.c_str();
|
||||
}
|
||||
TextInputStream& getInputStream()
|
||||
{
|
||||
return m_inputStream;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
2
tools/urt/libs/bytebool.cpp
Normal file
2
tools/urt/libs/bytebool.cpp
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include "bytebool.h"
|
11
tools/urt/libs/bytebool.h
Normal file
11
tools/urt/libs/bytebool.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#ifndef __BYTEBOOL__
|
||||
#define __BYTEBOOL__
|
||||
|
||||
// defines boolean and byte types usable in both c and c++ code
|
||||
// this header is not really meant for direct inclusion,
|
||||
// it is used by mathlib and cmdlib
|
||||
|
||||
typedef enum { qfalse, qtrue } qboolean;
|
||||
typedef unsigned char byte;
|
||||
|
||||
#endif
|
2
tools/urt/libs/bytestreamutils.cpp
Normal file
2
tools/urt/libs/bytestreamutils.cpp
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include "bytestreamutils.h"
|
100
tools/urt/libs/bytestreamutils.h
Normal file
100
tools/urt/libs/bytestreamutils.h
Normal file
|
@ -0,0 +1,100 @@
|
|||
|
||||
#if !defined(INCLUDED_BYTESTREAMUTILS_H)
|
||||
#define INCLUDED_BYTESTREAMUTILS_H
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
typedef signed short int16_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef signed int int32_t;
|
||||
typedef unsigned int uint32_t;
|
||||
|
||||
#else
|
||||
|
||||
#define _ISOC9X_SOURCE 1
|
||||
#define _ISOC99_SOURCE 1
|
||||
|
||||
#define __USE_ISOC9X 1
|
||||
#define __USE_ISOC99 1
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
template<typename InputStreamType, typename Type>
|
||||
inline void istream_read_little_endian(InputStreamType& istream, Type& value)
|
||||
{
|
||||
istream.read(reinterpret_cast<typename InputStreamType::byte_type*>(&value), sizeof(Type));
|
||||
#if defined(__BIG_ENDIAN__)
|
||||
std::reverse(reinterpret_cast<typename InputStreamType::byte_type*>(&value), reinterpret_cast<typename InputStreamType::byte_type*>(&value) + sizeof(Type));
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename InputStreamType, typename Type>
|
||||
inline void istream_read_big_endian(InputStreamType& istream, Type& value)
|
||||
{
|
||||
istream.read(reinterpret_cast<typename InputStreamType::byte_type*>(&value), sizeof(Type));
|
||||
#if !defined(__BIG_ENDIAN__)
|
||||
std::reverse(reinterpret_cast<typename InputStreamType::byte_type*>(&value), reinterpret_cast<typename InputStreamType::byte_type*>(&value) + sizeof(Type));
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename InputStreamType>
|
||||
inline void istream_read_byte(InputStreamType& istream, typename InputStreamType::byte_type& b)
|
||||
{
|
||||
istream.read(&b, 1);
|
||||
}
|
||||
|
||||
|
||||
template<typename InputStreamType>
|
||||
inline int16_t istream_read_int16_le(InputStreamType& istream)
|
||||
{
|
||||
int16_t value;
|
||||
istream_read_little_endian(istream, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename InputStreamType>
|
||||
inline uint16_t istream_read_uint16_le(InputStreamType& istream)
|
||||
{
|
||||
uint16_t value;
|
||||
istream_read_little_endian(istream, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename InputStreamType>
|
||||
inline int32_t istream_read_int32_le(InputStreamType& istream)
|
||||
{
|
||||
int32_t value;
|
||||
istream_read_little_endian(istream, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename InputStreamType>
|
||||
inline uint32_t istream_read_uint32_le(InputStreamType& istream)
|
||||
{
|
||||
uint32_t value;
|
||||
istream_read_little_endian(istream, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename InputStreamType>
|
||||
inline float istream_read_float32_le(InputStreamType& istream)
|
||||
{
|
||||
float value;
|
||||
istream_read_little_endian(istream, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename InputStreamType>
|
||||
inline typename InputStreamType::byte_type istream_read_byte(InputStreamType& istream)
|
||||
{
|
||||
typename InputStreamType::byte_type b;
|
||||
istream.read(&b, sizeof(typename InputStreamType::byte_type));
|
||||
return b;
|
||||
}
|
||||
|
||||
#endif
|
3
tools/urt/libs/character.cpp
Normal file
3
tools/urt/libs/character.cpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
#include "character.h"
|
||||
|
27
tools/urt/libs/character.h
Normal file
27
tools/urt/libs/character.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
|
||||
#if !defined(INCLUDED_CHARACTER_H)
|
||||
#define INCLUDED_CHARACTER_H
|
||||
|
||||
/// \file
|
||||
/// \brief Character encoding.
|
||||
|
||||
/// \brief Returns true if \p c is an ASCII character that can be represented with 7 bits.
|
||||
inline bool char_is_ascii(char c)
|
||||
{
|
||||
return (c & 0x80) == 0;
|
||||
}
|
||||
|
||||
/// \brief Returns true if \p string consists entirely of ASCII characters.
|
||||
inline bool string_is_ascii(const char* string)
|
||||
{
|
||||
while(*string != '\0')
|
||||
{
|
||||
if(!char_is_ascii(*string++))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
106
tools/urt/libs/cmdlib.h
Normal file
106
tools/urt/libs/cmdlib.h
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
This code is based on source provided under the terms of the Id Software
|
||||
LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the
|
||||
GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of
|
||||
LICENSE_ID, please contact Id Software immediately at info@idsoftware.com.
|
||||
|
||||
All changes and additions to the original source which have been developed by
|
||||
other contributors (see CONTRIBUTORS) are provided under the terms of the
|
||||
license the contributors choose (see LICENSE), to the extent permitted by the
|
||||
LICENSE_ID. If you did not receive a copy of the contributor license,
|
||||
please contact the GtkRadiant maintainers at info@gtkradiant.com immediately.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
//
|
||||
// start of shared cmdlib stuff
|
||||
//
|
||||
|
||||
#ifndef __CMDLIB__
|
||||
#define __CMDLIB__
|
||||
|
||||
#include <time.h>
|
||||
|
||||
|
||||
// TTimo started adding portability code:
|
||||
// return true if spawning was successful, false otherwise
|
||||
// on win32 we have a bCreateConsole flag to create a new console or run inside the current one
|
||||
//boolean Q_Exec(const char* pCmd, boolean bCreateConsole);
|
||||
// execute a system command:
|
||||
// cmd: the command to run
|
||||
// cmdline: the command line
|
||||
// NOTE TTimo following are win32 specific:
|
||||
// execdir: the directory to execute in
|
||||
// bCreateConsole: spawn a new console or not
|
||||
// return values;
|
||||
// if the spawn was fine
|
||||
// TODO TTimo add functionality to track the process until it dies
|
||||
|
||||
bool Q_Exec(const char *cmd, char *cmdline, const char *execdir, bool bCreateConsole);
|
||||
|
||||
// some easy portability crap
|
||||
|
||||
|
||||
#define access_owner_read 0400
|
||||
#define access_owner_write 0200
|
||||
#define access_owner_execute 0100
|
||||
#define access_owner_rw_ 0600
|
||||
#define access_owner_r_x 0500
|
||||
#define access_owner__wx 0300
|
||||
#define access_owner_rwx 0700
|
||||
|
||||
#define access_group_read 0040
|
||||
#define access_group_write 0020
|
||||
#define access_group_execute 0010
|
||||
#define access_group_rw_ 0060
|
||||
#define access_group_r_x 0050
|
||||
#define access_group__wx 0030
|
||||
#define access_group_rwx 0070
|
||||
|
||||
#define access_others_read 0004
|
||||
#define access_others_write 0002
|
||||
#define access_others_execute 0001
|
||||
#define access_others_rw_ 0006
|
||||
#define access_others_r_x 0005
|
||||
#define access_others__wx 0003
|
||||
#define access_others_rwx 0007
|
||||
|
||||
|
||||
#define access_rwxrwxr_x (access_owner_rwx | access_group_rwx | access_others_r_x)
|
||||
#define access_rwxrwxrwx (access_owner_rwx | access_group_rwx | access_others_rwx)
|
||||
|
||||
// Q_mkdir
|
||||
// returns true if succeeded in creating directory
|
||||
#ifdef WIN32
|
||||
#include <direct.h>
|
||||
inline bool Q_mkdir(const char* name)
|
||||
{
|
||||
return _mkdir(name) != -1;
|
||||
}
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
inline bool Q_mkdir(const char* name)
|
||||
{
|
||||
return mkdir(name, access_rwxrwxr_x) != -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
inline double Sys_DoubleTime(void)
|
||||
{
|
||||
return clock()/ 1000.0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
1
tools/urt/libs/cmdlib/.cvsignore
Normal file
1
tools/urt/libs/cmdlib/.cvsignore
Normal file
|
@ -0,0 +1 @@
|
|||
Debug
Release
*.ncb
*.opt
*.plg
*.001
*.BAK
|
3
tools/urt/libs/cmdlib/.cvswrappers
Normal file
3
tools/urt/libs/cmdlib/.cvswrappers
Normal file
|
@ -0,0 +1,3 @@
|
|||
*.dsp -m 'COPY' -k 'b'
|
||||
*.dsw -m 'COPY' -k 'b'
|
||||
*.scc -m 'COPY' -k 'b'
|
133
tools/urt/libs/cmdlib/cmdlib.cpp
Normal file
133
tools/urt/libs/cmdlib/cmdlib.cpp
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
This code is based on source provided under the terms of the Id Software
|
||||
LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the
|
||||
GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of
|
||||
LICENSE_ID, please contact Id Software immediately at info@idsoftware.com.
|
||||
|
||||
All changes and additions to the original source which have been developed by
|
||||
other contributors (see CONTRIBUTORS) are provided under the terms of the
|
||||
license the contributors choose (see LICENSE), to the extent permitted by the
|
||||
LICENSE_ID. If you did not receive a copy of the contributor license,
|
||||
please contact the GtkRadiant maintainers at info@gtkradiant.com immediately.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
//
|
||||
// start of shared cmdlib stuff
|
||||
//
|
||||
|
||||
#include "cmdlib.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "string/string.h"
|
||||
#include "os/path.h"
|
||||
#include "container/array.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#if defined (__linux__) || defined (__APPLE__)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
|
||||
#if defined (__linux__) || defined (__APPLE__)
|
||||
bool Q_Exec(const char *cmd, char *cmdline, const char *, bool)
|
||||
{
|
||||
char fullcmd[2048];
|
||||
char *pCmd;
|
||||
#ifdef _DEBUG
|
||||
printf("Q_Exec damnit\n");
|
||||
#endif
|
||||
switch (fork())
|
||||
{
|
||||
case -1:
|
||||
return true;
|
||||
break;
|
||||
case 0:
|
||||
// always concat the command on linux
|
||||
if (cmd)
|
||||
{
|
||||
strcpy(fullcmd, cmd);
|
||||
}
|
||||
else
|
||||
fullcmd[0] = '\0';
|
||||
if (cmdline)
|
||||
{
|
||||
strcat(fullcmd, " ");
|
||||
strcat(fullcmd, cmdline);
|
||||
}
|
||||
pCmd = fullcmd;
|
||||
while (*pCmd == ' ')
|
||||
pCmd++;
|
||||
#ifdef _DEBUG
|
||||
printf("Running system...\n");
|
||||
printf("Command: %s\n", pCmd);
|
||||
#endif
|
||||
system( pCmd );
|
||||
#ifdef _DEBUG
|
||||
printf ("system() returned\n");
|
||||
#endif
|
||||
_exit (0);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
// NOTE TTimo windows is VERY nitpicky about the syntax in CreateProcess
|
||||
bool Q_Exec(const char *cmd, char *cmdline, const char *execdir, bool bCreateConsole)
|
||||
{
|
||||
PROCESS_INFORMATION ProcessInformation;
|
||||
STARTUPINFO startupinfo = {0};
|
||||
DWORD dwCreationFlags;
|
||||
GetStartupInfo (&startupinfo);
|
||||
if (bCreateConsole)
|
||||
dwCreationFlags = CREATE_NEW_CONSOLE | NORMAL_PRIORITY_CLASS;
|
||||
else
|
||||
dwCreationFlags = DETACHED_PROCESS | NORMAL_PRIORITY_CLASS;
|
||||
const char *pCmd;
|
||||
char *pCmdline;
|
||||
pCmd = cmd;
|
||||
if (pCmd)
|
||||
{
|
||||
while (*pCmd == ' ')
|
||||
pCmd++;
|
||||
}
|
||||
pCmdline = cmdline;
|
||||
if (pCmdline)
|
||||
{
|
||||
while (*pCmdline == ' ')
|
||||
pCmdline++;
|
||||
}
|
||||
|
||||
if (CreateProcess(
|
||||
pCmd,
|
||||
pCmdline,
|
||||
NULL,
|
||||
NULL,
|
||||
FALSE,
|
||||
dwCreationFlags,
|
||||
NULL,
|
||||
execdir,
|
||||
&startupinfo,
|
||||
&ProcessInformation
|
||||
))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
101
tools/urt/libs/cmdlib/cmdlib.dsp
Normal file
101
tools/urt/libs/cmdlib/cmdlib.dsp
Normal file
|
@ -0,0 +1,101 @@
|
|||
# Microsoft Developer Studio Project File - Name="cmdlib" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Static Library" 0x0104
|
||||
|
||||
CFG=cmdlib - Win32 Debug
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "cmdlib.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "cmdlib.mak" CFG="cmdlib - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "cmdlib - Win32 Release" (based on "Win32 (x86) Static Library")
|
||||
!MESSAGE "cmdlib - Win32 Debug" (based on "Win32 (x86) Static Library")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName "cmdlib"
|
||||
# PROP Scc_LocalPath ".."
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "cmdlib - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Release"
|
||||
# PROP BASE Intermediate_Dir "Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Release"
|
||||
# PROP Intermediate_Dir "Release"
|
||||
# PROP Target_Dir ""
|
||||
MTL=midl.exe
|
||||
F90=df.exe
|
||||
# ADD BASE F90 /include:"Release/"
|
||||
# ADD F90 /include:"Release/"
|
||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
|
||||
# ADD CPP /nologo /MD /W3 /O2 /I "..\\" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
|
||||
# SUBTRACT CPP /YX
|
||||
# ADD BASE RSC /l 0x409
|
||||
# ADD RSC /l 0x409
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LIB32=link.exe -lib
|
||||
# ADD BASE LIB32 /nologo
|
||||
# ADD LIB32 /nologo
|
||||
|
||||
!ELSEIF "$(CFG)" == "cmdlib - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "Debug"
|
||||
# PROP BASE Intermediate_Dir "Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "Debug"
|
||||
# PROP Intermediate_Dir "Debug"
|
||||
# PROP Target_Dir ""
|
||||
MTL=midl.exe
|
||||
F90=df.exe
|
||||
# ADD BASE F90 /include:"Debug/"
|
||||
# ADD F90 /include:"Debug/"
|
||||
# ADD BASE CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c
|
||||
# ADD CPP /nologo /MDd /W3 /Z7 /Od /I "..\\" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c
|
||||
# ADD BASE RSC /l 0x409
|
||||
# ADD RSC /l 0x409
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LIB32=link.exe -lib
|
||||
# ADD BASE LIB32 /nologo
|
||||
# ADD LIB32 /nologo
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "cmdlib - Win32 Release"
|
||||
# Name "cmdlib - Win32 Debug"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\CMDLIB.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\cmdlib.h
|
||||
# End Source File
|
||||
# End Target
|
||||
# End Project
|
122
tools/urt/libs/cmdlib/cmdlib.vcproj
Normal file
122
tools/urt/libs/cmdlib/cmdlib.vcproj
Normal file
|
@ -0,0 +1,122 @@
|
|||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="7.10"
|
||||
Name="cmdlib"
|
||||
ProjectGUID="{8845C5C1-4154-425F-8643-447FADC03449}"
|
||||
Keyword="Win32Proj">
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"/>
|
||||
</Platforms>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="Debug"
|
||||
IntermediateDirectory="Debug"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="../"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
|
||||
StringPooling="TRUE"
|
||||
MinimalRebuild="TRUE"
|
||||
ExceptionHandling="FALSE"
|
||||
BasicRuntimeChecks="0"
|
||||
RuntimeLibrary="3"
|
||||
UsePrecompiledHeader="0"
|
||||
BrowseInformation="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="TRUE"
|
||||
DebugInformationFormat="4"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
OutputFile="$(OutDir)/cmdlib.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"
|
||||
IntermediateDirectory="Release"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
GlobalOptimizations="TRUE"
|
||||
InlineFunctionExpansion="2"
|
||||
EnableIntrinsicFunctions="TRUE"
|
||||
OptimizeForWindowsApplication="TRUE"
|
||||
AdditionalIncludeDirectories="../"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
|
||||
StringPooling="TRUE"
|
||||
ExceptionHandling="FALSE"
|
||||
RuntimeLibrary="2"
|
||||
BufferSecurityCheck="FALSE"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="TRUE"
|
||||
DebugInformationFormat="3"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
OutputFile="$(OutDir)/cmdlib.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>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="src"
|
||||
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
|
||||
<File
|
||||
RelativePath=".\cmdlib.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\cmdlib.h">
|
||||
</File>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
19
tools/urt/libs/container/array.cpp
Normal file
19
tools/urt/libs/container/array.cpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
|
||||
#include "array.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
class Bleh
|
||||
{
|
||||
Array<int> m_array;
|
||||
public:
|
||||
Bleh() : m_array(16)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
void testAutoArray()
|
||||
{
|
||||
Array<Bleh> array(32);
|
||||
}
|
||||
}
|
170
tools/urt/libs/container/array.h
Normal file
170
tools/urt/libs/container/array.h
Normal file
|
@ -0,0 +1,170 @@
|
|||
|
||||
#if !defined(INCLUDED_CONTAINER_ARRAY_H)
|
||||
#define INCLUDED_CONTAINER_ARRAY_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
|
||||
#include "memory/allocator.h"
|
||||
|
||||
/// \brief An array whose size is variable at run-time.
|
||||
///
|
||||
/// - Resizing the array destroys all the existing elements and invalidates all iterators.
|
||||
/// - Default-Constructible, Copyable, Assignable.
|
||||
/// - Compatible with the containers and algorithms in the Standard Template Library (STL) - http://www.sgi.com/tech/stl/
|
||||
///
|
||||
/// \param Element The type to be stored in the array. Must provide a default-constructor and a copy-constructor.
|
||||
/// \param Allocator A custom memory-allocator, conforming to the std::allocator interface.
|
||||
template<typename Element, typename Allocator = DefaultAllocator<Element> >
|
||||
class Array : public Allocator
|
||||
{
|
||||
std::size_t m_size;
|
||||
Element* m_data;
|
||||
|
||||
Element* construct(std::size_t size)
|
||||
{
|
||||
#if 1
|
||||
return New<Element, Allocator>(*this).vector(size);
|
||||
#else
|
||||
return new Element[size];
|
||||
#endif
|
||||
}
|
||||
template<typename T1>
|
||||
Element* construct(std::size_t size, const T1& value)
|
||||
{
|
||||
return New<Element, Allocator>(*this).vector(size, value);
|
||||
}
|
||||
void destroy(Element* data, std::size_t size)
|
||||
{
|
||||
#if 1
|
||||
Delete<Element, Allocator>(*this).vector(data, size);
|
||||
#else
|
||||
delete[] data;
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
typedef Element value_type;
|
||||
typedef value_type* iterator;
|
||||
typedef const value_type* const_iterator;
|
||||
|
||||
Array()
|
||||
: m_size(0), m_data(0)
|
||||
{
|
||||
}
|
||||
Array(std::size_t size)
|
||||
: m_size(size), m_data(construct(size))
|
||||
{
|
||||
}
|
||||
template<typename T1>
|
||||
Array(std::size_t size, const T1& value)
|
||||
: m_size(size), m_data(construct(size, value))
|
||||
{
|
||||
}
|
||||
Array(const Array& other)
|
||||
: Allocator(other), m_size(other.size()), m_data(construct(m_size))
|
||||
{
|
||||
std::copy(other.begin(), other.end(), begin());
|
||||
}
|
||||
template<typename Iterator>
|
||||
Array(Iterator start, Iterator finish)
|
||||
: m_size(std::distance(start, finish)), m_data(construct(m_size))
|
||||
{
|
||||
std::copy(start, finish, begin());
|
||||
}
|
||||
~Array()
|
||||
{
|
||||
destroy(m_data, m_size);
|
||||
}
|
||||
|
||||
Array& operator=(const Array& other)
|
||||
{
|
||||
Array temp(other);
|
||||
temp.swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(Array& other)
|
||||
{
|
||||
std::swap(m_size, other.m_size);
|
||||
std::swap(m_data, other.m_data);
|
||||
}
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
const_iterator begin() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
iterator end()
|
||||
{
|
||||
return m_data + m_size;
|
||||
}
|
||||
const_iterator end() const
|
||||
{
|
||||
return m_data + m_size;
|
||||
}
|
||||
|
||||
value_type& operator[](std::size_t index)
|
||||
{
|
||||
#if defined(_DEBUG)
|
||||
ASSERT_MESSAGE(index < size(), "array index out of bounds");
|
||||
#endif
|
||||
return m_data[index];
|
||||
}
|
||||
const value_type& operator[](std::size_t index) const
|
||||
{
|
||||
#if defined(_DEBUG)
|
||||
ASSERT_MESSAGE(index < size(), "array index out of bounds");
|
||||
#endif
|
||||
return m_data[index];
|
||||
}
|
||||
value_type* data()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
const value_type* data() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
std::size_t size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
bool empty() const
|
||||
{
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
void resize(std::size_t count)
|
||||
{
|
||||
if(count != size())
|
||||
{
|
||||
Array temp(count);
|
||||
temp.swap(*this);
|
||||
}
|
||||
}
|
||||
void resize(std::size_t count, const value_type& value)
|
||||
{
|
||||
if(count != size())
|
||||
{
|
||||
Array temp(count, value);
|
||||
temp.swap(*this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
namespace std
|
||||
{
|
||||
/// \brief Swaps the values of \p self and \p other.
|
||||
/// Overloads std::swap.
|
||||
template<typename Element, typename Allocator>
|
||||
inline void swap(Array<Element, Allocator>& self, Array<Element, Allocator>& other)
|
||||
{
|
||||
self.swap(other);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
2
tools/urt/libs/container/cache.cpp
Normal file
2
tools/urt/libs/container/cache.cpp
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include "cache.h"
|
177
tools/urt/libs/container/cache.h
Normal file
177
tools/urt/libs/container/cache.h
Normal file
|
@ -0,0 +1,177 @@
|
|||
|
||||
#if !defined(INCLUDED_CONTAINER_CACHE_H)
|
||||
#define INCLUDED_CONTAINER_CACHE_H
|
||||
|
||||
#include <cstddef>
|
||||
#include "container/hashtable.h"
|
||||
#include "memory/allocator.h"
|
||||
|
||||
template<typename Type, typename Parameter>
|
||||
class DefaultCreationPolicy
|
||||
{
|
||||
public:
|
||||
Type* construct(const Parameter& parameter)
|
||||
{
|
||||
return New<Type>().scalar(parameter);
|
||||
}
|
||||
void destroy(Type* p)
|
||||
{
|
||||
Delete<Type>().scalar(p);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
class SharedValue
|
||||
{
|
||||
typedef Type value_type;
|
||||
typedef value_type* pointer;
|
||||
typedef value_type& reference;
|
||||
|
||||
std::size_t m_count;
|
||||
pointer m_value;
|
||||
|
||||
public:
|
||||
SharedValue()
|
||||
: m_count(0), m_value(0)
|
||||
{
|
||||
}
|
||||
~SharedValue()
|
||||
{
|
||||
ASSERT_MESSAGE(m_count == 0 , "destroying a referenced object\n");
|
||||
}
|
||||
void set(pointer value)
|
||||
{
|
||||
m_value = value;
|
||||
}
|
||||
pointer get()
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
std::size_t increment()
|
||||
{
|
||||
return ++m_count;
|
||||
}
|
||||
std::size_t decrement()
|
||||
{
|
||||
ASSERT_MESSAGE(!empty(), "destroying a non-existent object\n");
|
||||
return --m_count;
|
||||
}
|
||||
std::size_t count()
|
||||
{
|
||||
return m_count;
|
||||
}
|
||||
bool empty()
|
||||
{
|
||||
return m_count == 0;
|
||||
}
|
||||
reference operator*() const
|
||||
{
|
||||
ASSERT_NOTNULL(m_value);
|
||||
return *m_value;
|
||||
}
|
||||
pointer operator->() const
|
||||
{
|
||||
return &(operator*());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// \brief Caches values that are uniquely identified by a key.
|
||||
///
|
||||
/// - Automatically removes objects that are no longer referenced.
|
||||
///
|
||||
/// \param Key Uniquely identifies each element.
|
||||
/// \param Cached The type to be cached. Must define a constructor that accepts \c Key.
|
||||
template<typename Key, typename Cached, typename Hasher, typename KeyEqual = std::equal_to<Key>, typename CreationPolicy = DefaultCreationPolicy<Cached, Key> >
|
||||
class HashedCache : public CreationPolicy
|
||||
{
|
||||
typedef SharedValue<Cached> Element;
|
||||
typedef HashTable<Key, Element, Hasher, KeyEqual> map_type;
|
||||
|
||||
map_type m_map;
|
||||
|
||||
public:
|
||||
explicit HashedCache(const CreationPolicy& creation = CreationPolicy())
|
||||
: CreationPolicy(creation), m_map(256)
|
||||
{
|
||||
}
|
||||
~HashedCache()
|
||||
{
|
||||
ASSERT_MESSAGE(empty(), "HashedCache::~HashedCache: not empty");
|
||||
}
|
||||
|
||||
typedef typename map_type::iterator iterator;
|
||||
typedef typename map_type::value_type value_type;
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
return m_map.begin();
|
||||
}
|
||||
iterator end()
|
||||
{
|
||||
return m_map.end();
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return m_map.empty();
|
||||
}
|
||||
|
||||
iterator find(const Key& key)
|
||||
{
|
||||
return m_map.find(key);
|
||||
}
|
||||
|
||||
void capture(iterator i)
|
||||
{
|
||||
(*i).value.increment();
|
||||
}
|
||||
void release(iterator i)
|
||||
{
|
||||
if((*i).value.decrement() == 0)
|
||||
{
|
||||
CreationPolicy::destroy((*i).value.get());
|
||||
m_map.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
#if 1
|
||||
Element& capture(const Key& key)
|
||||
{
|
||||
Element& elem = m_map[key];
|
||||
if(elem.increment() == 1)
|
||||
{
|
||||
elem.set(CreationPolicy::construct(key));
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
#else
|
||||
value_type& capture(const Key& key)
|
||||
{
|
||||
iterator i = m_map.find(key);
|
||||
if(i == m_map.end())
|
||||
{
|
||||
Element element;
|
||||
element.set(CreationPolicy::construct(key));
|
||||
i = m_map.insert(key, element);
|
||||
}
|
||||
(*i).value.increment();
|
||||
return (*i);
|
||||
}
|
||||
#endif
|
||||
void release(const Key& key)
|
||||
{
|
||||
iterator i = m_map.find(key);
|
||||
ASSERT_MESSAGE(i != m_map.end(), "releasing a non-existent object\n");
|
||||
release(i);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_map.clear();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
3
tools/urt/libs/container/container.cpp
Normal file
3
tools/urt/libs/container/container.cpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
#include "container.h"
|
||||
|
326
tools/urt/libs/container/container.h
Normal file
326
tools/urt/libs/container/container.h
Normal file
|
@ -0,0 +1,326 @@
|
|||
|
||||
#if !defined(INCLUDED_CONTAINER_CONTAINER_H)
|
||||
#define INCLUDED_CONTAINER_CONTAINER_H
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
|
||||
#include "generic/static.h"
|
||||
|
||||
/// \brief A single-value container, which can either be empty or full.
|
||||
template<typename Type>
|
||||
class Single
|
||||
{
|
||||
Type* m_value;
|
||||
public:
|
||||
Single() : m_value(0)
|
||||
{
|
||||
}
|
||||
bool empty()
|
||||
{
|
||||
return m_value == 0;
|
||||
}
|
||||
Type* insert(const Type& other)
|
||||
{
|
||||
m_value = new Type(other);
|
||||
return m_value;
|
||||
}
|
||||
void clear()
|
||||
{
|
||||
delete m_value;
|
||||
m_value = 0;
|
||||
}
|
||||
Type& get()
|
||||
{
|
||||
//ASSERT_MESSAGE(!empty(), "Single: must be initialised before being accessed");
|
||||
return *m_value;
|
||||
}
|
||||
const Type& get() const
|
||||
{
|
||||
//ASSERT_MESSAGE(!empty(), "Single: must be initialised before being accessed");
|
||||
return *m_value;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// \brief An adaptor to make std::list into a Unique Sequence - which cannot contain the same value more than once.
|
||||
/// \param Value Uniquely identifies itself. Must provide a copy-constructor and an equality operator.
|
||||
template<typename Value>
|
||||
class UnsortedSet
|
||||
{
|
||||
typedef typename std::list<Value> Values;
|
||||
Values m_values;
|
||||
public:
|
||||
typedef typename Values::iterator iterator;
|
||||
typedef typename Values::const_iterator const_iterator;
|
||||
typedef typename Values::reverse_iterator reverse_iterator;
|
||||
typedef typename Values::const_reverse_iterator const_reverse_iterator;
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
return m_values.begin();
|
||||
}
|
||||
const_iterator begin() const
|
||||
{
|
||||
return m_values.begin();
|
||||
}
|
||||
iterator end()
|
||||
{
|
||||
return m_values.end();
|
||||
}
|
||||
const_iterator end() const
|
||||
{
|
||||
return m_values.end();
|
||||
}
|
||||
reverse_iterator rbegin()
|
||||
{
|
||||
return m_values.rbegin();
|
||||
}
|
||||
const_reverse_iterator rbegin() const
|
||||
{
|
||||
return m_values.rbegin();
|
||||
}
|
||||
reverse_iterator rend()
|
||||
{
|
||||
return m_values.rend();
|
||||
}
|
||||
const_reverse_iterator rend() const
|
||||
{
|
||||
return m_values.rend();
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return m_values.empty();
|
||||
}
|
||||
std::size_t size() const
|
||||
{
|
||||
return m_values.size();
|
||||
}
|
||||
void clear()
|
||||
{
|
||||
m_values.clear();
|
||||
}
|
||||
|
||||
void swap(UnsortedSet& other)
|
||||
{
|
||||
std::swap(m_values, other.m_values);
|
||||
}
|
||||
iterator insert(const Value& value)
|
||||
{
|
||||
ASSERT_MESSAGE(find(value) == end(), "UnsortedSet::insert: already added");
|
||||
m_values.push_back(value);
|
||||
return --end();
|
||||
}
|
||||
void erase(const Value& value)
|
||||
{
|
||||
iterator i = find(value);
|
||||
ASSERT_MESSAGE(i != end(), "UnsortedSet::erase: not found");
|
||||
m_values.erase(i);
|
||||
}
|
||||
iterator find(const Value& value)
|
||||
{
|
||||
return std::find(begin(), end(), value);
|
||||
}
|
||||
};
|
||||
|
||||
namespace std
|
||||
{
|
||||
/// \brief Swaps the values of \p self and \p other.
|
||||
/// Overloads std::swap.
|
||||
template<typename Value>
|
||||
inline void swap(UnsortedSet<Value>& self, UnsortedSet<Value>& other)
|
||||
{
|
||||
self.swap(other);
|
||||
}
|
||||
}
|
||||
|
||||
/// An adaptor to make std::list into a Unique Associative Sequence - which cannot contain the same value more than once.
|
||||
/// Key: Uniquely identifies a value. Must provide a copy-constructor and an equality operator.
|
||||
/// Value: Must provide a copy-constructor.
|
||||
template<typename Key, typename Value>
|
||||
class UnsortedMap
|
||||
{
|
||||
typedef typename std::list< std::pair<Key, Value> > Values;
|
||||
Values m_values;
|
||||
public:
|
||||
typedef typename Values::value_type value_type;
|
||||
typedef typename Values::iterator iterator;
|
||||
typedef typename Values::const_iterator const_iterator;
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
return m_values.begin();
|
||||
}
|
||||
const_iterator begin() const
|
||||
{
|
||||
return m_values.begin();
|
||||
}
|
||||
iterator end()
|
||||
{
|
||||
return m_values.end();
|
||||
}
|
||||
const_iterator end() const
|
||||
{
|
||||
return m_values.end();
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return m_values.empty();
|
||||
}
|
||||
std::size_t size() const
|
||||
{
|
||||
return m_values.size();
|
||||
}
|
||||
void clear()
|
||||
{
|
||||
m_values.clear();
|
||||
}
|
||||
|
||||
iterator insert(const value_type& value)
|
||||
{
|
||||
ASSERT_MESSAGE(find(value.first) == end(), "UnsortedMap::insert: already added");
|
||||
m_values.push_back(value);
|
||||
return --m_values.end();
|
||||
}
|
||||
void erase(const Key& key)
|
||||
{
|
||||
iterator i = find(key);
|
||||
ASSERT_MESSAGE(i != end(), "UnsortedMap::erase: not found");
|
||||
erase(i);
|
||||
}
|
||||
void erase(iterator i)
|
||||
{
|
||||
m_values.erase(i);
|
||||
}
|
||||
iterator find(const Key& key)
|
||||
{
|
||||
for(iterator i = m_values.begin(); i != m_values.end(); ++i)
|
||||
{
|
||||
if((*i).first == key)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return m_values.end();
|
||||
}
|
||||
const_iterator find(const Key& key) const
|
||||
{
|
||||
for(const_iterator i = m_values.begin(); i != m_values.end(); ++i)
|
||||
{
|
||||
if((*i).first == key)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return m_values.end();
|
||||
}
|
||||
|
||||
Value& operator[](const Key& key)
|
||||
{
|
||||
iterator i = find(key);
|
||||
if(i != end())
|
||||
{
|
||||
return (*i).second;
|
||||
}
|
||||
|
||||
m_values.push_back(Values::value_type(key, Value()));
|
||||
return m_values.back().second;
|
||||
}
|
||||
};
|
||||
|
||||
/// An adaptor to assert when duplicate values are added, or non-existent values removed from a std::set.
|
||||
template<typename Value>
|
||||
class UniqueSet
|
||||
{
|
||||
typedef std::set<Value> Values;
|
||||
Values m_values;
|
||||
public:
|
||||
typedef typename Values::iterator iterator;
|
||||
typedef typename Values::const_iterator const_iterator;
|
||||
typedef typename Values::reverse_iterator reverse_iterator;
|
||||
typedef typename Values::const_reverse_iterator const_reverse_iterator;
|
||||
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
return m_values.begin();
|
||||
}
|
||||
const_iterator begin() const
|
||||
{
|
||||
return m_values.begin();
|
||||
}
|
||||
iterator end()
|
||||
{
|
||||
return m_values.end();
|
||||
}
|
||||
const_iterator end() const
|
||||
{
|
||||
return m_values.end();
|
||||
}
|
||||
reverse_iterator rbegin()
|
||||
{
|
||||
return m_values.rbegin();
|
||||
}
|
||||
const_reverse_iterator rbegin() const
|
||||
{
|
||||
return m_values.rbegin();
|
||||
}
|
||||
reverse_iterator rend()
|
||||
{
|
||||
return m_values.rend();
|
||||
}
|
||||
const_reverse_iterator rend() const
|
||||
{
|
||||
return m_values.rend();
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return m_values.empty();
|
||||
}
|
||||
std::size_t size() const
|
||||
{
|
||||
return m_values.size();
|
||||
}
|
||||
void clear()
|
||||
{
|
||||
m_values.clear();
|
||||
}
|
||||
|
||||
void swap(UniqueSet& other)
|
||||
{
|
||||
std::swap(m_values, other.m_values);
|
||||
}
|
||||
iterator insert(const Value& value)
|
||||
{
|
||||
std::pair<iterator, bool> result = m_values.insert(value);
|
||||
ASSERT_MESSAGE(result.second, "UniqueSet::insert: already added");
|
||||
return result.first;
|
||||
}
|
||||
void erase(const Value& value)
|
||||
{
|
||||
iterator i = find(value);
|
||||
ASSERT_MESSAGE(i != end(), "UniqueSet::erase: not found");
|
||||
m_values.erase(i);
|
||||
}
|
||||
iterator find(const Value& value)
|
||||
{
|
||||
return std::find(begin(), end(), value);
|
||||
}
|
||||
};
|
||||
|
||||
namespace std
|
||||
{
|
||||
/// \brief Swaps the values of \p self and \p other.
|
||||
/// Overloads std::swap.
|
||||
template<typename Value>
|
||||
inline void swap(UniqueSet<Value>& self, UniqueSet<Value>& other)
|
||||
{
|
||||
self.swap(other);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
2
tools/urt/libs/container/hashfunc.cpp
Normal file
2
tools/urt/libs/container/hashfunc.cpp
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include "hashfunc.h"
|
413
tools/urt/libs/container/hashfunc.h
Normal file
413
tools/urt/libs/container/hashfunc.h
Normal file
|
@ -0,0 +1,413 @@
|
|||
|
||||
#if !defined(INCLUDED_CONTAINER_HASHFUNC_H)
|
||||
#define INCLUDED_CONTAINER_HASHFUNC_H
|
||||
|
||||
/*
|
||||
--------------------------------------------------------------------
|
||||
lookup2.c, by Bob Jenkins, December 1996, Public Domain.
|
||||
hash(), hash2(), hash3, and mix() are externally useful functions.
|
||||
Routines to test the hash are included if SELF_TEST is defined.
|
||||
You can use this free for any purpose. It has no warranty.
|
||||
--------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <cctype>
|
||||
#include "string/string.h"
|
||||
#include "container/array.h"
|
||||
typedef unsigned long int ub4; /* unsigned 4-byte quantities */
|
||||
typedef unsigned char ub1;
|
||||
|
||||
inline ub1 ub1_as_ub1_nocase(ub1 byte)
|
||||
{
|
||||
return std::tolower(byte);
|
||||
}
|
||||
|
||||
inline ub4 ub1x4_as_ub4_nocase(const ub1 bytes[4])
|
||||
{
|
||||
ub4 result;
|
||||
reinterpret_cast<ub1*>(&result)[0] = ub1_as_ub1_nocase(bytes[0]);
|
||||
reinterpret_cast<ub1*>(&result)[1] = ub1_as_ub1_nocase(bytes[1]);
|
||||
reinterpret_cast<ub1*>(&result)[2] = ub1_as_ub1_nocase(bytes[2]);
|
||||
reinterpret_cast<ub1*>(&result)[3] = ub1_as_ub1_nocase(bytes[3]);
|
||||
return result;
|
||||
}
|
||||
|
||||
class ub1_default_traits
|
||||
{
|
||||
public:
|
||||
static ub1 as_ub1(ub1 byte)
|
||||
{
|
||||
return byte;
|
||||
}
|
||||
};
|
||||
|
||||
class ub1_nocase_traits
|
||||
{
|
||||
public:
|
||||
static ub1 as_ub1(ub1 byte)
|
||||
{
|
||||
return ub1_as_ub1_nocase(byte);
|
||||
}
|
||||
};
|
||||
|
||||
class ub1x4_default_traits
|
||||
{
|
||||
public:
|
||||
static ub4 as_ub4(const ub1 bytes[4])
|
||||
{
|
||||
return *reinterpret_cast<const ub4*>(bytes);
|
||||
}
|
||||
};
|
||||
|
||||
class ub1x4_nocase_traits
|
||||
{
|
||||
public:
|
||||
static ub4 as_ub4(const ub1 bytes[4])
|
||||
{
|
||||
return ub1x4_as_ub4_nocase(bytes);
|
||||
}
|
||||
};
|
||||
|
||||
class ub4_default_traits
|
||||
{
|
||||
public:
|
||||
static ub4 as_ub4(ub4 i)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
};
|
||||
|
||||
class ub4_nocase_traits
|
||||
{
|
||||
public:
|
||||
static ub4 as_ub4(ub4 i)
|
||||
{
|
||||
return ub1x4_as_ub4_nocase(reinterpret_cast<const ub1*>(&i));
|
||||
}
|
||||
};
|
||||
|
||||
#define hashsize(n) ((ub4)1<<(n))
|
||||
#define hashmask(n) (hashsize(n)-1)
|
||||
|
||||
/*
|
||||
--------------------------------------------------------------------
|
||||
mix -- mix 3 32-bit values reversibly.
|
||||
For every delta with one or two bit set, and the deltas of all three
|
||||
high bits or all three low bits, whether the original value of a,b,c
|
||||
is almost all zero or is uniformly distributed,
|
||||
* If mix() is run forward or backward, at least 32 bits in a,b,c
|
||||
have at least 1/4 probability of changing.
|
||||
* If mix() is run forward, every bit of c will change between 1/3 and
|
||||
2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.)
|
||||
mix() was built out of 36 single-cycle latency instructions in a
|
||||
structure that could supported 2x parallelism, like so:
|
||||
a -= b;
|
||||
a -= c; x = (c>>13);
|
||||
b -= c; a ^= x;
|
||||
b -= a; x = (a<<8);
|
||||
c -= a; b ^= x;
|
||||
c -= b; x = (b>>13);
|
||||
...
|
||||
Unfortunately, superscalar Pentiums and Sparcs can't take advantage
|
||||
of that parallelism. They've also turned some of those single-cycle
|
||||
latency instructions into multi-cycle latency instructions. Still,
|
||||
this is the fastest good hash I could find. There were about 2^^68
|
||||
to choose from. I only looked at a billion or so.
|
||||
--------------------------------------------------------------------
|
||||
*/
|
||||
#define mix(a,b,c) \
|
||||
{ \
|
||||
a -= b; a -= c; a ^= (c>>13); \
|
||||
b -= c; b -= a; b ^= (a<<8); \
|
||||
c -= a; c -= b; c ^= (b>>13); \
|
||||
a -= b; a -= c; a ^= (c>>12); \
|
||||
b -= c; b -= a; b ^= (a<<16); \
|
||||
c -= a; c -= b; c ^= (b>>5); \
|
||||
a -= b; a -= c; a ^= (c>>3); \
|
||||
b -= c; b -= a; b ^= (a<<10); \
|
||||
c -= a; c -= b; c ^= (b>>15); \
|
||||
}
|
||||
|
||||
/* same, but slower, works on systems that might have 8 byte ub4's */
|
||||
#define mix2(a,b,c) \
|
||||
{ \
|
||||
a -= b; a -= c; a ^= (c>>13); \
|
||||
b -= c; b -= a; b ^= (a<< 8); \
|
||||
c -= a; c -= b; c ^= ((b&0xffffffff)>>13); \
|
||||
a -= b; a -= c; a ^= ((c&0xffffffff)>>12); \
|
||||
b -= c; b -= a; b = (b ^ (a<<16)) & 0xffffffff; \
|
||||
c -= a; c -= b; c = (c ^ (b>> 5)) & 0xffffffff; \
|
||||
a -= b; a -= c; a = (a ^ (c>> 3)) & 0xffffffff; \
|
||||
b -= c; b -= a; b = (b ^ (a<<10)) & 0xffffffff; \
|
||||
c -= a; c -= b; c = (c ^ (b>>15)) & 0xffffffff; \
|
||||
}
|
||||
|
||||
/*
|
||||
--------------------------------------------------------------------
|
||||
hash() -- hash a variable-length key into a 32-bit value
|
||||
k : the key (the unaligned variable-length array of bytes)
|
||||
len : the length of the key, counting by bytes
|
||||
level : can be any 4-byte value
|
||||
Returns a 32-bit value. Every bit of the key affects every bit of
|
||||
the return value. Every 1-bit and 2-bit delta achieves avalanche.
|
||||
About 36+6len instructions.
|
||||
|
||||
The best hash table sizes are powers of 2. There is no need to do
|
||||
mod a prime (mod is sooo slow!). If you need less than 32 bits,
|
||||
use a bitmask. For example, if you need only 10 bits, do
|
||||
h = (h & hashmask(10));
|
||||
In which case, the hash table should have hashsize(10) elements.
|
||||
|
||||
If you are hashing n strings (ub1 **)k, do it like this:
|
||||
for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h);
|
||||
|
||||
By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this
|
||||
code any way you wish, private, educational, or commercial. It's free.
|
||||
|
||||
See http://burlteburtle.net/bob/hash/evahash.html
|
||||
Use for hash table lookup, or anything where one collision in 2^32 is
|
||||
acceptable. Do NOT use for cryptographic purposes.
|
||||
--------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
template<typename UB1Traits, typename UB4x1Traits>
|
||||
inline ub4 hash(
|
||||
const ub1 *k, /* the key */
|
||||
ub4 length, /* the length of the key */
|
||||
ub4 initval, /* the previous hash, or an arbitrary value */
|
||||
const UB1Traits& ub1traits,
|
||||
const UB4x1Traits& ub4x1traits
|
||||
)
|
||||
{
|
||||
register ub4 a,b,c,len;
|
||||
|
||||
/* Set up the internal state */
|
||||
len = length;
|
||||
a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
|
||||
c = initval; /* the previous hash value */
|
||||
|
||||
/*---------------------------------------- handle most of the key */
|
||||
while (len >= 12)
|
||||
{
|
||||
a += (k[0] +((ub4)UB1Traits::as_ub1(k[1])<<8) +((ub4)UB1Traits::as_ub1(k[2])<<16) +((ub4)UB1Traits::as_ub1(k[3])<<24));
|
||||
b += (k[4] +((ub4)UB1Traits::as_ub1(k[5])<<8) +((ub4)UB1Traits::as_ub1(k[6])<<16) +((ub4)UB1Traits::as_ub1(k[7])<<24));
|
||||
c += (k[8] +((ub4)UB1Traits::as_ub1(k[9])<<8) +((ub4)UB1Traits::as_ub1(k[10])<<16)+((ub4)UB1Traits::as_ub1(k[11])<<24));
|
||||
mix(a,b,c);
|
||||
k += 12; len -= 12;
|
||||
}
|
||||
|
||||
/*------------------------------------- handle the last 11 bytes */
|
||||
c += length;
|
||||
switch(len) /* all the case statements fall through */
|
||||
{
|
||||
case 11: c += ((ub4)UB1Traits::as_ub1(k[10]) << 24);
|
||||
case 10: c += ((ub4)UB1Traits::as_ub1(k[9]) << 16);
|
||||
case 9 : c += ((ub4)UB1Traits::as_ub1(k[8]) << 8);
|
||||
/* the first byte of c is reserved for the length */
|
||||
case 8 : b += ((ub4)UB1Traits::as_ub1(k[7]) << 24);
|
||||
case 7 : b += ((ub4)UB1Traits::as_ub1(k[6]) << 16);
|
||||
case 6 : b += ((ub4)UB1Traits::as_ub1(k[5]) << 8);
|
||||
case 5 : b += UB1Traits::as_ub1(k[4]);
|
||||
case 4 : a += ((ub4)UB1Traits::as_ub1(k[3]) << 24);
|
||||
case 3 : a += ((ub4)UB1Traits::as_ub1(k[2]) << 16);
|
||||
case 2 : a += ((ub4)UB1Traits::as_ub1(k[1]) << 8);
|
||||
case 1 : a += UB1Traits::as_ub1(k[0]);
|
||||
/* case 0: nothing left to add */
|
||||
}
|
||||
mix(a,b,c);
|
||||
/*-------------------------------------------- report the result */
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
--------------------------------------------------------------------
|
||||
This works on all machines. hash2() is identical to hash() on
|
||||
little-endian machines, except that the length has to be measured
|
||||
in ub4s instead of bytes. It is much faster than hash(). It
|
||||
requires
|
||||
-- that the key be an array of ub4's, and
|
||||
-- that all your machines have the same endianness, and
|
||||
-- that the length be the number of ub4's in the key
|
||||
--------------------------------------------------------------------
|
||||
*/
|
||||
template<typename UB4Traits>
|
||||
inline ub4 hash2(
|
||||
const ub4 *k, /* the key */
|
||||
ub4 length, /* the length of the key, in ub4s */
|
||||
ub4 initval, /* the previous hash, or an arbitrary value */
|
||||
const UB4Traits& ub4traits
|
||||
)
|
||||
{
|
||||
register ub4 a,b,c,len;
|
||||
|
||||
/* Set up the internal state */
|
||||
len = length;
|
||||
a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
|
||||
c = initval; /* the previous hash value */
|
||||
|
||||
/*---------------------------------------- handle most of the key */
|
||||
while (len >= 3)
|
||||
{
|
||||
a += UB4Traits::as_ub4(k[0]);
|
||||
b += UB4Traits::as_ub4(k[1]);
|
||||
c += UB4Traits::as_ub4(k[2]);
|
||||
mix(a,b,c);
|
||||
k += 3; len -= 3;
|
||||
}
|
||||
|
||||
/*-------------------------------------- handle the last 2 ub4's */
|
||||
c += length;
|
||||
switch(len) /* all the case statements fall through */
|
||||
{
|
||||
/* c is reserved for the length */
|
||||
case 2 : b += UB4Traits::as_ub4(k[1]);
|
||||
case 1 : a += UB4Traits::as_ub4(k[0]);
|
||||
/* case 0: nothing left to add */
|
||||
}
|
||||
mix(a,b,c);
|
||||
/*-------------------------------------------- report the result */
|
||||
return c;
|
||||
}
|
||||
|
||||
typedef ub4 hash_t;
|
||||
|
||||
inline hash_t hash_ub1(const ub1* key, std::size_t len, hash_t previous = 0)
|
||||
{
|
||||
return hash(key, ub4(len), previous, ub1_default_traits(), ub1x4_default_traits());
|
||||
}
|
||||
|
||||
inline hash_t hash_ub1_nocase(const ub1* key, std::size_t len, hash_t previous = 0)
|
||||
{
|
||||
return hash(key, ub4(len), previous, ub1_nocase_traits(), ub1x4_nocase_traits());
|
||||
}
|
||||
|
||||
template<typename UB4Traits>
|
||||
inline hash_t hash_ub4(const ub4* key, std::size_t len, const UB4Traits& traits, hash_t previous = 0)
|
||||
{
|
||||
return hash2(key,ub4(len), previous, traits);
|
||||
}
|
||||
|
||||
inline ub4 hash_combine(ub4 left, ub4 right)
|
||||
{
|
||||
return hash_ub1(reinterpret_cast<const ub1*>(&left), 4, right);
|
||||
}
|
||||
|
||||
template<typename POD>
|
||||
inline hash_t pod_hash(const POD& pod)
|
||||
{
|
||||
return hash_ub1(reinterpret_cast<const ub1*>(&pod), sizeof(POD));
|
||||
}
|
||||
|
||||
inline hash_t string_hash(const char* string, hash_t previous = 0)
|
||||
{
|
||||
return hash_ub1(reinterpret_cast<const ub1*>(string), string_length(string), previous);
|
||||
}
|
||||
|
||||
inline hash_t string_hash_nocase(const char* string, hash_t previous = 0)
|
||||
{
|
||||
return hash_ub1_nocase(reinterpret_cast<const ub1*>(string), string_length(string), previous);
|
||||
}
|
||||
|
||||
struct HashString
|
||||
{
|
||||
typedef hash_t hash_type;
|
||||
hash_type operator()(const CopiedString& string) const
|
||||
{
|
||||
return string_hash(string.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
struct HashStringNoCase
|
||||
{
|
||||
typedef hash_t hash_type;
|
||||
hash_type operator()(const CopiedString& string) const
|
||||
{
|
||||
return string_hash_nocase(string.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Length of a string in ub4.
|
||||
/// "wibble" (6) gives 2,
|
||||
/// "and" (3) gives 1,
|
||||
/// "bleh" (4) gives 2
|
||||
inline std::size_t string_length_ub4(const char* string)
|
||||
{
|
||||
return ((string_length(string)>>2)+1)<<2;
|
||||
}
|
||||
|
||||
/// \brief Hashable key type that stores a string as an array of ub4 - making hashing faster.
|
||||
/// Also caches the 32-bit result of the hash to speed up comparison of keys.
|
||||
template<typename UB4Traits = ub4_default_traits>
|
||||
class HashKey
|
||||
{
|
||||
Array<ub4> m_key;
|
||||
hash_t m_hash;
|
||||
|
||||
void copy(const HashKey& other)
|
||||
{
|
||||
std::copy(other.m_key.begin(), other.m_key.end(), m_key.begin());
|
||||
m_hash = other.m_hash;
|
||||
}
|
||||
void copy(const char* string)
|
||||
{
|
||||
strncpy(reinterpret_cast<char*>(m_key.data()), string, m_key.size());
|
||||
for(Array<ub4>::iterator i = m_key.begin(); i != m_key.end(); ++i)
|
||||
{
|
||||
*i = UB4Traits::as_ub4(*i);
|
||||
}
|
||||
m_hash = hash_ub4(m_key.data(), m_key.size(), ub4_default_traits());
|
||||
}
|
||||
bool equal(const HashKey& other) const
|
||||
{
|
||||
return m_hash == other.m_hash && m_key.size() == other.m_key.size()
|
||||
&& std::equal(m_key.begin(), m_key.end(), other.m_key.begin());
|
||||
}
|
||||
|
||||
public:
|
||||
HashKey(const HashKey& other) : m_key(other.m_key.size())
|
||||
{
|
||||
copy(other);
|
||||
}
|
||||
HashKey(const char* string) : m_key(string_length_ub4(string))
|
||||
{
|
||||
copy(string);
|
||||
}
|
||||
HashKey& operator=(const char* string)
|
||||
{
|
||||
m_key.resize(string_length_ub4(string));
|
||||
copy(string);
|
||||
return *this;
|
||||
}
|
||||
bool operator==(const HashKey& other) const
|
||||
{
|
||||
return equal(other);
|
||||
}
|
||||
bool operator!=(const HashKey& other) const
|
||||
{
|
||||
return !equal(other);
|
||||
}
|
||||
hash_t hash() const
|
||||
{
|
||||
return m_hash;
|
||||
}
|
||||
#if 0
|
||||
const char* c_str() const
|
||||
{
|
||||
return reinterpret_cast<const char*>(m_key.data());
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
/// \brief Hash function to use with HashKey.
|
||||
struct HashKeyHasher
|
||||
{
|
||||
typedef hash_t hash_type;
|
||||
hash_type operator()(const HashKey<ub4_default_traits>& key) const
|
||||
{
|
||||
return key.hash();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
45
tools/urt/libs/container/hashtable.cpp
Normal file
45
tools/urt/libs/container/hashtable.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
|
||||
#include "hashtable.h"
|
||||
|
||||
#if defined(_DEBUG) || defined(DOXYGEN)
|
||||
|
||||
#include "hashfunc.h"
|
||||
|
||||
namespace ExampleHashTable
|
||||
{
|
||||
void testStuff()
|
||||
{
|
||||
// HashTable example
|
||||
typedef HashTable<CopiedString, int, HashString> MyHashTable;
|
||||
MyHashTable hashtable;
|
||||
hashtable["bleh"] = 5;
|
||||
hashtable.insert("blah", 17);
|
||||
hashtable["foo"] = 99;
|
||||
hashtable.insert("bar", 23);
|
||||
|
||||
int bleh = (*hashtable.find("bleh")).value; // 5
|
||||
int blah = hashtable["blah"]; // 17
|
||||
hashtable.erase("foo");
|
||||
MyHashTable::iterator barIter = hashtable.find("bar");
|
||||
hashtable.erase(barIter);
|
||||
|
||||
for(MyHashTable::iterator i = hashtable.begin(); i != hashtable.end(); ++i)
|
||||
{
|
||||
if((*i).key != "bleh")
|
||||
{
|
||||
++hashtable["count"]; // insertion does not invalidate iterators
|
||||
}
|
||||
}
|
||||
// end example
|
||||
}
|
||||
|
||||
struct Always
|
||||
{
|
||||
Always()
|
||||
{
|
||||
testStuff();
|
||||
}
|
||||
} always;
|
||||
}
|
||||
|
||||
#endif
|
455
tools/urt/libs/container/hashtable.h
Normal file
455
tools/urt/libs/container/hashtable.h
Normal file
|
@ -0,0 +1,455 @@
|
|||
|
||||
#if !defined(INCLUDED_CONTAINER_HASHTABLE_H)
|
||||
#define INCLUDED_CONTAINER_HASHTABLE_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include "debugging/debugging.h"
|
||||
|
||||
|
||||
namespace HashTableDetail
|
||||
{
|
||||
inline std::size_t next_power_of_two(std::size_t size)
|
||||
{
|
||||
std::size_t result = 1;
|
||||
while(result < size)
|
||||
{
|
||||
result <<= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
struct BucketNodeBase
|
||||
{
|
||||
BucketNodeBase* next;
|
||||
BucketNodeBase* prev;
|
||||
};
|
||||
|
||||
inline void list_initialise(BucketNodeBase& self)
|
||||
{
|
||||
self.next = self.prev = &self;
|
||||
}
|
||||
|
||||
inline void list_swap(BucketNodeBase& self, BucketNodeBase& other)
|
||||
{
|
||||
BucketNodeBase tmp(self);
|
||||
if(other.next == &other)
|
||||
{
|
||||
list_initialise(self);
|
||||
}
|
||||
else
|
||||
{
|
||||
self = other;
|
||||
self.next->prev = self.prev->next = &self;
|
||||
}
|
||||
if(tmp.next == &self)
|
||||
{
|
||||
list_initialise(other);
|
||||
}
|
||||
else
|
||||
{
|
||||
other = tmp;
|
||||
other.next->prev = other.prev->next = &other;
|
||||
}
|
||||
}
|
||||
|
||||
inline void node_link(BucketNodeBase* node, BucketNodeBase* next)
|
||||
{
|
||||
node->next = next;
|
||||
node->prev = next->prev;
|
||||
next->prev = node;
|
||||
node->prev->next = node;
|
||||
}
|
||||
inline void node_unlink(BucketNodeBase* node)
|
||||
{
|
||||
node->prev->next = node->next;
|
||||
node->next->prev = node->prev;
|
||||
}
|
||||
|
||||
template<typename Key, typename Value>
|
||||
struct KeyValue
|
||||
{
|
||||
const Key key;
|
||||
Value value;
|
||||
|
||||
KeyValue(const Key& key_, const Value& value_)
|
||||
: key(key_), value(value_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Key, typename Value, typename Hash>
|
||||
struct BucketNode : public BucketNodeBase
|
||||
{
|
||||
Hash m_hash;
|
||||
KeyValue<Key, Value> m_value;
|
||||
|
||||
BucketNode(Hash hash, const Key& key, const Value& value)
|
||||
: m_hash(hash), m_value(key, value)
|
||||
{
|
||||
}
|
||||
BucketNode* getNext()
|
||||
{
|
||||
return static_cast<BucketNode*>(next);
|
||||
}
|
||||
BucketNode* getPrev()
|
||||
{
|
||||
return static_cast<BucketNode*>(prev);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Key, typename Value, typename Hash>
|
||||
class BucketIterator
|
||||
{
|
||||
typedef BucketNode<Key, Value, Hash> Node;
|
||||
Node* m_node;
|
||||
|
||||
void increment()
|
||||
{
|
||||
m_node = m_node->getNext();
|
||||
}
|
||||
|
||||
public:
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef difference_type distance_type;
|
||||
typedef KeyValue<Key, Value> value_type;
|
||||
typedef value_type* pointer;
|
||||
typedef value_type& reference;
|
||||
|
||||
BucketIterator(Node* node) : m_node(node)
|
||||
{
|
||||
}
|
||||
|
||||
Node* node()
|
||||
{
|
||||
return m_node;
|
||||
}
|
||||
|
||||
bool operator==(const BucketIterator& other) const
|
||||
{
|
||||
return m_node == other.m_node;
|
||||
}
|
||||
bool operator!=(const BucketIterator& other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
BucketIterator& operator++()
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
BucketIterator operator++(int)
|
||||
{
|
||||
BucketIterator tmp = *this;
|
||||
increment();
|
||||
return tmp;
|
||||
}
|
||||
value_type& operator*()
|
||||
{
|
||||
return m_node->m_value;
|
||||
}
|
||||
value_type* operator->()
|
||||
{
|
||||
return &(operator*());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// A hash-table container which maps keys to values.
|
||||
///
|
||||
/// - Inserting or removing elements does not invalidate iterators.
|
||||
/// - Inserting or retrieving an element for a given key takes O(1) time on average.
|
||||
/// - Elements are stored in no particular order.
|
||||
///
|
||||
/// \param Key Uniquely identifies a value. Must provide a copy-constructor.
|
||||
/// \param Value The value to be stored . Must provide a default-constructor and a copy-constructor.
|
||||
/// \param Hasher Must provide 'std::size_t operator()(const Key&) const' which always returns the same result if the same argument is given.
|
||||
/// \param KeyEqual Must provide 'bool operator==(const Key&, const Key&) const' which returns true only if both arguments are equal.
|
||||
///
|
||||
/// \dontinclude container/hashtable.cpp
|
||||
/// \skipline HashTable example
|
||||
/// \until end example
|
||||
template<typename Key, typename Value, typename Hasher, typename KeyEqual = std::equal_to<Key> >
|
||||
class HashTable : private KeyEqual, private Hasher
|
||||
{
|
||||
typedef typename Hasher::hash_type hash_type;
|
||||
typedef HashTableDetail::KeyValue<Key, Value> KeyValue;
|
||||
typedef HashTableDetail::BucketNode<Key, Value, hash_type> BucketNode;
|
||||
|
||||
inline BucketNode* node_create(hash_type hash, const Key& key, const Value& value)
|
||||
{
|
||||
return new BucketNode(hash, key, value);
|
||||
}
|
||||
inline void node_destroy(BucketNode* node)
|
||||
{
|
||||
delete node;
|
||||
}
|
||||
|
||||
typedef BucketNode* Bucket;
|
||||
|
||||
static Bucket* buckets_new(std::size_t count)
|
||||
{
|
||||
Bucket* buckets = new Bucket[count];
|
||||
std::uninitialized_fill(buckets, buckets + count, Bucket(0));
|
||||
return buckets;
|
||||
}
|
||||
static void buckets_delete(Bucket* buckets)
|
||||
{
|
||||
delete[] buckets;
|
||||
}
|
||||
|
||||
std::size_t m_bucketCount;
|
||||
Bucket* m_buckets;
|
||||
std::size_t m_size;
|
||||
HashTableDetail::BucketNodeBase m_list;
|
||||
|
||||
BucketNode* getFirst()
|
||||
{
|
||||
return static_cast<BucketNode*>(m_list.next);
|
||||
}
|
||||
BucketNode* getLast()
|
||||
{
|
||||
return static_cast<BucketNode*>(&m_list);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
typedef KeyValue value_type;
|
||||
typedef HashTableDetail::BucketIterator<Key, Value, hash_type> iterator;
|
||||
|
||||
private:
|
||||
|
||||
void initialise()
|
||||
{
|
||||
list_initialise(m_list);
|
||||
}
|
||||
hash_type hashKey(const Key& key)
|
||||
{
|
||||
return Hasher::operator()(key);
|
||||
}
|
||||
|
||||
std::size_t getBucketId(hash_type hash) const
|
||||
{
|
||||
return hash & (m_bucketCount - 1);
|
||||
}
|
||||
Bucket& getBucket(hash_type hash)
|
||||
{
|
||||
return m_buckets[getBucketId(hash)];
|
||||
}
|
||||
BucketNode* bucket_find(Bucket bucket, hash_type hash, const Key& key)
|
||||
{
|
||||
std::size_t bucketId = getBucketId(hash);
|
||||
for(iterator i(bucket); i != end(); ++i)
|
||||
{
|
||||
hash_type nodeHash = i.node()->m_hash;
|
||||
|
||||
if(getBucketId(nodeHash) != bucketId)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(nodeHash == hash && KeyEqual::operator()((*i).key, key))
|
||||
{
|
||||
return i.node();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
BucketNode* bucket_insert(Bucket& bucket, BucketNode* node)
|
||||
{
|
||||
// link node into list
|
||||
node_link(node, bucket_next(bucket));
|
||||
bucket = node;
|
||||
return node;
|
||||
}
|
||||
BucketNode* bucket_next(Bucket& bucket)
|
||||
{
|
||||
Bucket* end = m_buckets + m_bucketCount;
|
||||
for(Bucket* i = &bucket; i != end; ++i)
|
||||
{
|
||||
if(*i != 0)
|
||||
{
|
||||
return *i;
|
||||
}
|
||||
}
|
||||
return getLast();
|
||||
}
|
||||
|
||||
void buckets_resize(std::size_t count)
|
||||
{
|
||||
BucketNode* first = getFirst();
|
||||
BucketNode* last = getLast();
|
||||
|
||||
buckets_delete(m_buckets);
|
||||
|
||||
m_bucketCount = count;
|
||||
|
||||
m_buckets = buckets_new(m_bucketCount);
|
||||
initialise();
|
||||
|
||||
for(BucketNode* i = first; i != last;)
|
||||
{
|
||||
BucketNode* node = i;
|
||||
i = i->getNext();
|
||||
bucket_insert(getBucket((*node).m_hash), node);
|
||||
}
|
||||
}
|
||||
void size_increment()
|
||||
{
|
||||
if(m_size == m_bucketCount)
|
||||
{
|
||||
buckets_resize(m_bucketCount == 0 ? 8 : m_bucketCount << 1);
|
||||
}
|
||||
++m_size;
|
||||
}
|
||||
void size_decrement()
|
||||
{
|
||||
--m_size;
|
||||
}
|
||||
|
||||
HashTable(const HashTable& other);
|
||||
HashTable& operator=(const HashTable& other);
|
||||
public:
|
||||
HashTable() : m_bucketCount(0), m_buckets(0), m_size(0)
|
||||
{
|
||||
initialise();
|
||||
}
|
||||
HashTable(std::size_t bucketCount) : m_bucketCount(HashTableDetail::next_power_of_two(bucketCount)), m_buckets(buckets_new(m_bucketCount)), m_size(0)
|
||||
{
|
||||
initialise();
|
||||
}
|
||||
~HashTable()
|
||||
{
|
||||
for(BucketNode* i = getFirst(); i != getLast();)
|
||||
{
|
||||
BucketNode* node = i;
|
||||
i = i->getNext();
|
||||
node_destroy(node);
|
||||
}
|
||||
buckets_delete(m_buckets);
|
||||
}
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
return iterator(getFirst());
|
||||
}
|
||||
iterator end()
|
||||
{
|
||||
return iterator(getLast());
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return m_size == 0;
|
||||
}
|
||||
std::size_t size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
/// \brief Returns an iterator pointing to the value associated with \p key if it is contained by the hash-table, else \c end().
|
||||
iterator find(const Key& key)
|
||||
{
|
||||
hash_type hash = hashKey(key);
|
||||
if(m_bucketCount != 0)
|
||||
{
|
||||
Bucket bucket = getBucket(hash);
|
||||
if(bucket != 0)
|
||||
{
|
||||
BucketNode* node = bucket_find(bucket, hash, key);
|
||||
if(node != 0)
|
||||
{
|
||||
return iterator(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return end();
|
||||
}
|
||||
/// \brief Adds \p value to the hash-table associated with \p key if it does not exist, or replaces the current value associated with \p key.
|
||||
iterator insert(const Key& key, const Value& value)
|
||||
{
|
||||
hash_type hash = hashKey(key);
|
||||
if(m_bucketCount != 0)
|
||||
{
|
||||
Bucket& bucket = getBucket(hash);
|
||||
if(bucket != 0)
|
||||
{
|
||||
BucketNode* node = bucket_find(bucket, hash, key);
|
||||
if(node != 0)
|
||||
{
|
||||
node->m_value.value = value;
|
||||
return iterator(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_increment();
|
||||
return iterator(bucket_insert(getBucket(hash), node_create(hash, key, value)));
|
||||
}
|
||||
|
||||
/// \brief Removes the value pointed to by \p i from the hash-table.
|
||||
///
|
||||
/// \p i must be a deferenceable iterator into the hash-table.
|
||||
void erase(iterator i)
|
||||
{
|
||||
Bucket& bucket = getBucket(i.node()->m_hash);
|
||||
BucketNode* node = i.node();
|
||||
|
||||
// if this was the last node in the bucket
|
||||
if(bucket == node)
|
||||
{
|
||||
bucket = (node->getNext() == getLast() || &getBucket(node->getNext()->m_hash) != &bucket) ? 0 : node->getNext();
|
||||
}
|
||||
|
||||
node_unlink(node);
|
||||
ASSERT_MESSAGE(node != 0, "tried to erase a non-existent key/value");
|
||||
node_destroy(node);
|
||||
|
||||
size_decrement();
|
||||
}
|
||||
|
||||
/// \brief Returns the value identified by \p key if it is contained by the hash-table, else inserts and returns a new default-constructed value associated with \p key.
|
||||
Value& operator[](const Key& key)
|
||||
{
|
||||
hash_type hash = hashKey(key);
|
||||
if(m_bucketCount != 0)
|
||||
{
|
||||
Bucket& bucket = getBucket(hash);
|
||||
if(bucket != 0)
|
||||
{
|
||||
BucketNode* node = bucket_find(bucket, hash, key);
|
||||
if(node != 0)
|
||||
{
|
||||
return node->m_value.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
size_increment();
|
||||
return bucket_insert(getBucket(hash), node_create(hash, key, Value()))->m_value.value;
|
||||
}
|
||||
/// \brief Removes the value associated with \p key from the hash-table.
|
||||
void erase(const Key& key)
|
||||
{
|
||||
erase(find(key));
|
||||
}
|
||||
/// \brief Swaps the contents of the hash-table with \p other.
|
||||
void swap(HashTable& other)
|
||||
{
|
||||
std::swap(m_buckets, other.m_buckets);
|
||||
std::swap(m_bucketCount, other.m_bucketCount);
|
||||
std::swap(m_size, other.m_size);
|
||||
HashTableDetail::list_swap(m_list, other.m_list);
|
||||
}
|
||||
/// \brief Removes all values from the hash-table.
|
||||
void clear()
|
||||
{
|
||||
HashTable tmp;
|
||||
tmp.swap(*this);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
3
tools/urt/libs/container/stack.cpp
Normal file
3
tools/urt/libs/container/stack.cpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
#include "stack.h"
|
||||
|
219
tools/urt/libs/container/stack.h
Normal file
219
tools/urt/libs/container/stack.h
Normal file
|
@ -0,0 +1,219 @@
|
|||
|
||||
#if !defined(INCLUDED_CONTAINER_STACK_H)
|
||||
#define INCLUDED_CONTAINER_STACK_H
|
||||
|
||||
#include "memory/allocator.h"
|
||||
#include <algorithm>
|
||||
|
||||
/// \brief A stack whose storage capacity is variable at run-time. Similar to std::vector.
|
||||
///
|
||||
/// - Pushing or popping elements is a constant-time operation (on average).
|
||||
/// - The storage capacity of the stack will grow when a new element is added beyond the current capacity. Iterators are invalidated when the storage capacity grows.
|
||||
/// - DefaultConstructible, Copyable, Assignable.
|
||||
/// - Compatible with the containers and algorithms in the Standard Template Library (STL) - http://www.sgi.com/tech/stl/
|
||||
///
|
||||
/// \param Type: The type to be stored in the stack. Must provide a copy-constructor.
|
||||
template<typename Type>
|
||||
class Stack : public DefaultAllocator<Type>
|
||||
{
|
||||
typedef DefaultAllocator<Type> Allocator;
|
||||
|
||||
enum
|
||||
{
|
||||
DEFAULT_CAPACITY = 4,
|
||||
};
|
||||
|
||||
typedef Type* pointer;
|
||||
typedef const Type* const_pointer;
|
||||
|
||||
public:
|
||||
typedef const_pointer const_iterator;
|
||||
private:
|
||||
|
||||
pointer m_data;
|
||||
pointer m_end;
|
||||
std::size_t m_capacity;
|
||||
|
||||
|
||||
void insert(const Type& value)
|
||||
{
|
||||
Allocator::construct(m_end++, value);
|
||||
}
|
||||
void insert_overflow(const Type& value)
|
||||
{
|
||||
const std::size_t new_capacity = (m_capacity) ? m_capacity + m_capacity : std::size_t(DEFAULT_CAPACITY);
|
||||
const pointer new_data = Allocator::allocate(new_capacity);
|
||||
const pointer new_end = std::copy(m_data, m_end, new_data);
|
||||
|
||||
destroy();
|
||||
Allocator::deallocate(m_data, m_capacity);
|
||||
|
||||
m_capacity = new_capacity;
|
||||
m_data = new_data;
|
||||
m_end = new_end;
|
||||
insert(value);
|
||||
}
|
||||
void destroy()
|
||||
{
|
||||
for(pointer p = m_data; p != m_end; ++p)
|
||||
{
|
||||
Allocator::destroy(p);
|
||||
}
|
||||
}
|
||||
void construct(const Stack& other)
|
||||
{
|
||||
pointer p = m_data;
|
||||
for(const_iterator i = other.begin(); i != other.end(); ++i)
|
||||
{
|
||||
Allocator::construct(p++, *i);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Stack() :
|
||||
m_data(0),
|
||||
m_end(0),
|
||||
m_capacity(0)
|
||||
{
|
||||
}
|
||||
Stack(const Type& value) :
|
||||
m_data(0),
|
||||
m_end(0),
|
||||
m_capacity(0)
|
||||
{
|
||||
push(value);
|
||||
}
|
||||
Stack(const Stack& other) :
|
||||
DefaultAllocator<Type>(other)
|
||||
{
|
||||
m_capacity = other.m_capacity;
|
||||
m_data = Allocator::allocate(m_capacity);
|
||||
construct(other);
|
||||
m_end = m_data + other.size();
|
||||
}
|
||||
~Stack()
|
||||
{
|
||||
destroy();
|
||||
Allocator::deallocate(m_data, m_capacity);
|
||||
}
|
||||
|
||||
const_iterator begin() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
const_iterator end() const
|
||||
{
|
||||
return m_end;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return end() == begin();
|
||||
}
|
||||
void clear()
|
||||
{
|
||||
destroy();
|
||||
m_end = m_data;
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
return m_end - m_data;
|
||||
}
|
||||
Type operator[](const std::size_t i) const
|
||||
{
|
||||
return m_data[i];
|
||||
}
|
||||
/// \brief Pushes \p value onto the stack at the top element. If reserved storage is insufficient for the new element, this will invalidate all iterators.
|
||||
void push(const Type& value)
|
||||
{
|
||||
if(size() == m_capacity)
|
||||
{
|
||||
insert_overflow(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
insert(value);
|
||||
}
|
||||
}
|
||||
/// \brief Removes the top element of the stack.
|
||||
void pop()
|
||||
{
|
||||
Allocator::destroy(--m_end);
|
||||
}
|
||||
/// \brief Returns the top element of the mutable stack.
|
||||
Type& top()
|
||||
{
|
||||
return *(m_end-1);
|
||||
}
|
||||
/// \brief Returns the top element of the non-mutable stack.
|
||||
const Type& top() const
|
||||
{
|
||||
return *(m_end-1);
|
||||
}
|
||||
/// \brief Returns the element below the top element of the mutable stack.
|
||||
Type& parent()
|
||||
{
|
||||
return *(m_end-2);
|
||||
}
|
||||
/// \brief Returns the element below the top element of the non-mutable stack.
|
||||
const Type& parent() const
|
||||
{
|
||||
return *(m_end-2);
|
||||
}
|
||||
/// \brief Swaps the values of this stack and \p other.
|
||||
void swap(Stack& other)
|
||||
{
|
||||
std::swap(m_data, other.m_data);
|
||||
std::swap(m_end, other.m_end);
|
||||
std::swap(m_capacity, other.m_capacity);
|
||||
}
|
||||
#if 1 // use copy-swap technique
|
||||
Stack& operator=(const Stack& other)
|
||||
{
|
||||
Stack temp(other);
|
||||
temp.swap(*this);
|
||||
return *this;
|
||||
}
|
||||
#else // avoids memory allocation if capacity is already sufficient.
|
||||
Stack& operator=(const Stack& other)
|
||||
{
|
||||
if(&other != this)
|
||||
{
|
||||
destroy();
|
||||
|
||||
if(other.size() > m_capacity)
|
||||
{
|
||||
Allocator::deallocate(m_data, m_capacity);
|
||||
m_capacity = other.m_capacity;
|
||||
m_data = Allocator::allocate(m_capacity);
|
||||
}
|
||||
m_end = m_data + other.size();
|
||||
|
||||
construct(other);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
/// \brief Returns true if \p self is lexicographically less than \p other.
|
||||
template<typename Type>
|
||||
inline bool operator<(const Stack<Type>& self, const Stack<Type>& other)
|
||||
{
|
||||
return std::lexicographical_compare(self.begin(), self.end(), other.begin(), other.end());
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
/// \brief Swaps the values of \p self and \p other.
|
||||
/// Overloads std::swap().
|
||||
template<typename Type>
|
||||
inline void swap(Stack<Type>& self, Stack<Type>& other)
|
||||
{
|
||||
self.swap(other);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
3
tools/urt/libs/convert.cpp
Normal file
3
tools/urt/libs/convert.cpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
#include "convert.h"
|
||||
|
285
tools/urt/libs/convert.h
Normal file
285
tools/urt/libs/convert.h
Normal file
|
@ -0,0 +1,285 @@
|
|||
|
||||
#if !defined(INCLUDED_CONVERT_H)
|
||||
#define INCLUDED_CONVERT_H
|
||||
|
||||
/// \file
|
||||
/// \brief Character encoding conversion.
|
||||
|
||||
#include "debugging/debugging.h"
|
||||
#include <algorithm>
|
||||
#include <glib/gunicode.h>
|
||||
#include <glib/gconvert.h>
|
||||
|
||||
#include "character.h"
|
||||
|
||||
/// \brief Returns the number of bytes required to represent \p character in UTF-8 encoding.
|
||||
inline std::size_t utf8_character_length(const char* character)
|
||||
{
|
||||
if((*character & 0xE0) == 0xC0) // 110xxxxx
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
else if((*character & 0xF0) == 0xE0) // 1110xxxx
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
else if((*character & 0xF8) == 0xF0) // 11110xxx
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
else if((*character & 0xFC) == 0xF8) // 111110xx
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
else if((*character & 0xFE) == 0xFC) // 1111110x
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
ERROR_MESSAGE("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct UTF8Character
|
||||
{
|
||||
const char* buffer;
|
||||
std::size_t length;
|
||||
UTF8Character() : buffer(0), length(0)
|
||||
{
|
||||
}
|
||||
UTF8Character(const char* bytes) : buffer(bytes), length(utf8_character_length(bytes))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator<(const UTF8Character& self, const UTF8Character& other)
|
||||
{
|
||||
return std::lexicographical_compare(self.buffer, self.buffer + self.length, other.buffer, other.buffer + other.length);
|
||||
}
|
||||
|
||||
/// \brief Writes \p c to \p ostream in Hex form. Useful for debugging.
|
||||
template<typename TextOutputStreamType>
|
||||
inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const UTF8Character& c)
|
||||
{
|
||||
for(const char* p = c.buffer; p != c.buffer + c.length; ++p)
|
||||
{
|
||||
ostream << HexChar(*p);
|
||||
}
|
||||
return ostream;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// \brief The character-set encoding for the current C locale.
|
||||
///
|
||||
/// Obtain the global instance with globalCharacterSet().
|
||||
class CharacterSet
|
||||
{
|
||||
const char* m_charSet;
|
||||
public:
|
||||
CharacterSet()
|
||||
{
|
||||
if(g_get_charset(&m_charSet) != FALSE)
|
||||
{
|
||||
m_charSet = 0;
|
||||
}
|
||||
}
|
||||
bool isUTF8() const
|
||||
{
|
||||
return m_charSet == 0;
|
||||
}
|
||||
const char* get() const
|
||||
{
|
||||
return m_charSet;
|
||||
}
|
||||
};
|
||||
|
||||
typedef LazyStatic<CharacterSet> GlobalCharacterSet;
|
||||
|
||||
/// \brief Returns the global instance of CharacterSet.
|
||||
inline CharacterSet& globalCharacterSet()
|
||||
{
|
||||
return GlobalCharacterSet::instance();
|
||||
}
|
||||
|
||||
|
||||
class UTF8CharacterToExtendedASCII
|
||||
{
|
||||
public:
|
||||
UTF8Character m_utf8;
|
||||
char m_c;
|
||||
UTF8CharacterToExtendedASCII() : m_c('\0')
|
||||
{
|
||||
}
|
||||
UTF8CharacterToExtendedASCII(const UTF8Character& utf8, char c) : m_utf8(utf8), m_c(c)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator<(const UTF8CharacterToExtendedASCII& self, const UTF8CharacterToExtendedASCII& other)
|
||||
{
|
||||
return self.m_utf8 < other.m_utf8;
|
||||
}
|
||||
|
||||
inline std::size_t extended_ascii_to_index(char c)
|
||||
{
|
||||
return static_cast<std::size_t>(c & 0x7F);
|
||||
}
|
||||
|
||||
inline char extended_ascii_for_index(std::size_t i)
|
||||
{
|
||||
return static_cast<char>(i | 0x80);
|
||||
}
|
||||
|
||||
/// \brief The active extended-ascii character set encoding.
|
||||
/// Performs UTF-8 encoding and decoding of extended-ascii characters.
|
||||
///
|
||||
/// Obtain the global instance with globalExtendedASCIICharacterSet().
|
||||
class ExtendedASCIICharacterSet
|
||||
{
|
||||
typedef char UTF8CharBuffer[6];
|
||||
UTF8CharBuffer m_converted[128];
|
||||
UTF8Character m_decodeMap[128];
|
||||
UTF8CharacterToExtendedASCII m_encodeMap[128];
|
||||
public:
|
||||
ExtendedASCIICharacterSet()
|
||||
{
|
||||
if(!globalCharacterSet().isUTF8())
|
||||
{
|
||||
GIConv descriptor = g_iconv_open("UTF-8", globalCharacterSet().get());
|
||||
for(std::size_t i = 1; i < 128; ++i)
|
||||
{
|
||||
char c = extended_ascii_for_index(i);
|
||||
char* inbuf = &c;
|
||||
std::size_t inbytesleft = 1;
|
||||
char* outbuf = m_converted[i];
|
||||
std::size_t outbytesleft = 6;
|
||||
if(g_iconv(descriptor, &inbuf, &inbytesleft, &outbuf, &outbytesleft) != (size_t)(-1))
|
||||
{
|
||||
UTF8Character utf8(m_converted[i]);
|
||||
m_decodeMap[i] = utf8;
|
||||
m_encodeMap[i] = UTF8CharacterToExtendedASCII(utf8, c);
|
||||
}
|
||||
}
|
||||
g_iconv_close(descriptor);
|
||||
std::sort(m_encodeMap, m_encodeMap + 128);
|
||||
}
|
||||
}
|
||||
/// \brief Prints the (up to) 128 characters in the current extended-ascii character set.
|
||||
/// Useful for debugging.
|
||||
void print() const
|
||||
{
|
||||
globalOutputStream() << "UTF-8 conversion required from charset: " << globalCharacterSet().get() << "\n";
|
||||
for(std::size_t i = 1; i < 128; ++i)
|
||||
{
|
||||
if(m_decodeMap[i].buffer != 0)
|
||||
{
|
||||
globalOutputStream() << extended_ascii_for_index(i) << " = " << m_decodeMap[i] << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
/// \brief Returns \p c decoded from extended-ascii to UTF-8.
|
||||
/// \p c must be an extended-ascii character.
|
||||
const UTF8Character& decode(char c) const
|
||||
{
|
||||
ASSERT_MESSAGE(!globalCharacterSet().isUTF8(), "locale is utf8, no conversion required");
|
||||
ASSERT_MESSAGE(!char_is_ascii(c), "decode: ascii character");
|
||||
ASSERT_MESSAGE(m_decodeMap[extended_ascii_to_index(c)].buffer != 0, "decode: invalid character: " << HexChar(c));
|
||||
return m_decodeMap[extended_ascii_to_index(c)];
|
||||
}
|
||||
/// \brief Returns \p c encoded to extended-ascii from UTF-8.
|
||||
/// \p c must map to an extended-ascii character.
|
||||
char encode(const UTF8Character& c) const
|
||||
{
|
||||
ASSERT_MESSAGE(!globalCharacterSet().isUTF8(), "locale is utf8, no conversion required");
|
||||
ASSERT_MESSAGE(!char_is_ascii(*c.buffer), "encode: ascii character");
|
||||
std::pair<const UTF8CharacterToExtendedASCII*, const UTF8CharacterToExtendedASCII*> range
|
||||
= std::equal_range(m_encodeMap, m_encodeMap + 128, UTF8CharacterToExtendedASCII(c, 0));
|
||||
ASSERT_MESSAGE(range.first != range.second, "encode: invalid character: " << c);
|
||||
return (*range.first).m_c;
|
||||
}
|
||||
};
|
||||
|
||||
typedef LazyStatic<ExtendedASCIICharacterSet> GlobalExtendedASCIICharacterSet;
|
||||
|
||||
/// \brief Returns the global instance of ExtendedASCIICharacterSet.
|
||||
inline ExtendedASCIICharacterSet& globalExtendedASCIICharacterSet()
|
||||
{
|
||||
return GlobalExtendedASCIICharacterSet::instance();
|
||||
}
|
||||
|
||||
class ConvertUTF8ToLocale
|
||||
{
|
||||
public:
|
||||
StringRange m_range;
|
||||
ConvertUTF8ToLocale(const char* string) : m_range(StringRange(string, string + strlen(string)))
|
||||
{
|
||||
}
|
||||
ConvertUTF8ToLocale(const StringRange& range) : m_range(range)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Writes \p convert to \p ostream after encoding each character to extended-ascii from UTF-8.
|
||||
template<typename TextOutputStreamType>
|
||||
inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const ConvertUTF8ToLocale& convert)
|
||||
{
|
||||
if(globalCharacterSet().isUTF8())
|
||||
{
|
||||
return ostream << convert.m_range;
|
||||
}
|
||||
|
||||
for(const char* p = convert.m_range.begin; p != convert.m_range.end;)
|
||||
{
|
||||
if(!char_is_ascii(*p))
|
||||
{
|
||||
UTF8Character c(p);
|
||||
ostream << globalExtendedASCIICharacterSet().encode(c);
|
||||
p += c.length;
|
||||
}
|
||||
else
|
||||
{
|
||||
ostream << *p++;
|
||||
}
|
||||
}
|
||||
return ostream;
|
||||
}
|
||||
|
||||
|
||||
class ConvertLocaleToUTF8
|
||||
{
|
||||
public:
|
||||
StringRange m_range;
|
||||
ConvertLocaleToUTF8(const char* string) : m_range(StringRange(string, string + strlen(string)))
|
||||
{
|
||||
}
|
||||
ConvertLocaleToUTF8(const StringRange& range) : m_range(range)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Writes \p convert to \p ostream after decoding each character from extended-ascii to UTF-8.
|
||||
template<typename TextOutputStreamType>
|
||||
inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const ConvertLocaleToUTF8& convert)
|
||||
{
|
||||
if(globalCharacterSet().isUTF8())
|
||||
{
|
||||
return ostream << convert.m_range;
|
||||
}
|
||||
|
||||
for(const char* p = convert.m_range.begin; p != convert.m_range.end; ++p)
|
||||
{
|
||||
if(!char_is_ascii(*p))
|
||||
{
|
||||
UTF8Character c(globalExtendedASCIICharacterSet().decode(*p));
|
||||
ostream.write(c.buffer, c.length);
|
||||
}
|
||||
else
|
||||
{
|
||||
ostream << *p;
|
||||
}
|
||||
}
|
||||
return ostream;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
250
tools/urt/libs/ddslib.h
Normal file
250
tools/urt/libs/ddslib.h
Normal file
|
@ -0,0 +1,250 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
|
||||
DDS Library
|
||||
|
||||
Based on code from Nvidia's DDS example:
|
||||
http://www.nvidia.com/object/dxtc_decompression_code.html
|
||||
|
||||
Copyright (c) 2003 Randy Reddig
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
Neither the names of the copyright holders nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
|
||||
/* marker */
|
||||
#ifndef DDSLIB_H
|
||||
#define DDSLIB_H
|
||||
|
||||
|
||||
|
||||
/* dependencies */
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
|
||||
|
||||
|
||||
/* c++ marker */
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* dds definition */
|
||||
typedef enum
|
||||
{
|
||||
DDS_PF_ARGB8888,
|
||||
DDS_PF_DXT1,
|
||||
DDS_PF_DXT2,
|
||||
DDS_PF_DXT3,
|
||||
DDS_PF_DXT4,
|
||||
DDS_PF_DXT5,
|
||||
DDS_PF_UNKNOWN
|
||||
}
|
||||
ddsPF_t;
|
||||
|
||||
|
||||
/* 16bpp stuff */
|
||||
#define DDS_LOW_5 0x001F;
|
||||
#define DDS_MID_6 0x07E0;
|
||||
#define DDS_HIGH_5 0xF800;
|
||||
#define DDS_MID_555 0x03E0;
|
||||
#define DDS_HI_555 0x7C00;
|
||||
|
||||
|
||||
/* structures */
|
||||
typedef struct ddsColorKey_s
|
||||
{
|
||||
unsigned int colorSpaceLowValue;
|
||||
unsigned int colorSpaceHighValue;
|
||||
}
|
||||
ddsColorKey_t;
|
||||
|
||||
|
||||
typedef struct ddsCaps_s
|
||||
{
|
||||
unsigned int caps1;
|
||||
unsigned int caps2;
|
||||
unsigned int caps3;
|
||||
unsigned int caps4;
|
||||
}
|
||||
ddsCaps_t;
|
||||
|
||||
|
||||
typedef struct ddsMultiSampleCaps_s
|
||||
{
|
||||
unsigned short flipMSTypes;
|
||||
unsigned short bltMSTypes;
|
||||
}
|
||||
ddsMultiSampleCaps_t;
|
||||
|
||||
|
||||
typedef struct ddsPixelFormat_s
|
||||
{
|
||||
unsigned int size;
|
||||
unsigned int flags;
|
||||
unsigned int fourCC;
|
||||
union
|
||||
{
|
||||
unsigned int rgbBitCount;
|
||||
unsigned int yuvBitCount;
|
||||
unsigned int zBufferBitDepth;
|
||||
unsigned int alphaBitDepth;
|
||||
unsigned int luminanceBitCount;
|
||||
unsigned int bumpBitCount;
|
||||
unsigned int privateFormatBitCount;
|
||||
};
|
||||
union
|
||||
{
|
||||
unsigned int rBitMask;
|
||||
unsigned int yBitMask;
|
||||
unsigned int stencilBitDepth;
|
||||
unsigned int luminanceBitMask;
|
||||
unsigned int bumpDuBitMask;
|
||||
unsigned int operations;
|
||||
};
|
||||
union
|
||||
{
|
||||
unsigned int gBitMask;
|
||||
unsigned int uBitMask;
|
||||
unsigned int zBitMask;
|
||||
unsigned int bumpDvBitMask;
|
||||
ddsMultiSampleCaps_t multiSampleCaps;
|
||||
};
|
||||
union
|
||||
{
|
||||
unsigned int bBitMask;
|
||||
unsigned int vBitMask;
|
||||
unsigned int stencilBitMask;
|
||||
unsigned int bumpLuminanceBitMask;
|
||||
};
|
||||
union
|
||||
{
|
||||
unsigned int rgbAlphaBitMask;
|
||||
unsigned int yuvAlphaBitMask;
|
||||
unsigned int luminanceAlphaBitMask;
|
||||
unsigned int rgbZBitMask;
|
||||
unsigned int yuvZBitMask;
|
||||
};
|
||||
}
|
||||
ddsPixelFormat_t;
|
||||
|
||||
|
||||
typedef struct ddsBuffer_s
|
||||
{
|
||||
/* magic: 'dds ' */
|
||||
char magic[ 4 ];
|
||||
|
||||
/* directdraw surface */
|
||||
unsigned int size;
|
||||
unsigned int flags;
|
||||
unsigned int height;
|
||||
unsigned int width;
|
||||
union
|
||||
{
|
||||
int pitch;
|
||||
unsigned int linearSize;
|
||||
};
|
||||
unsigned int backBufferCount;
|
||||
union
|
||||
{
|
||||
unsigned int mipMapCount;
|
||||
unsigned int refreshRate;
|
||||
unsigned int srcVBHandle;
|
||||
};
|
||||
unsigned int alphaBitDepth;
|
||||
unsigned int reserved;
|
||||
void *surface;
|
||||
union
|
||||
{
|
||||
ddsColorKey_t ckDestOverlay;
|
||||
unsigned int emptyFaceColor;
|
||||
};
|
||||
ddsColorKey_t ckDestBlt;
|
||||
ddsColorKey_t ckSrcOverlay;
|
||||
ddsColorKey_t ckSrcBlt;
|
||||
union
|
||||
{
|
||||
ddsPixelFormat_t pixelFormat;
|
||||
unsigned int fvf;
|
||||
};
|
||||
ddsCaps_t ddsCaps;
|
||||
unsigned int textureStage;
|
||||
|
||||
/* data (Varying size) */
|
||||
unsigned char data[ 4 ];
|
||||
}
|
||||
ddsBuffer_t;
|
||||
|
||||
|
||||
typedef struct ddsColorBlock_s
|
||||
{
|
||||
unsigned short colors[ 2 ];
|
||||
unsigned char row[ 4 ];
|
||||
}
|
||||
ddsColorBlock_t;
|
||||
|
||||
|
||||
typedef struct ddsAlphaBlockExplicit_s
|
||||
{
|
||||
unsigned short row[ 4 ];
|
||||
}
|
||||
ddsAlphaBlockExplicit_t;
|
||||
|
||||
|
||||
typedef struct ddsAlphaBlock3BitLinear_s
|
||||
{
|
||||
unsigned char alpha0;
|
||||
unsigned char alpha1;
|
||||
unsigned char stuff[ 6 ];
|
||||
}
|
||||
ddsAlphaBlock3BitLinear_t;
|
||||
|
||||
|
||||
typedef struct ddsColor_s
|
||||
{
|
||||
unsigned char r, g, b, a;
|
||||
}
|
||||
ddsColor_t;
|
||||
|
||||
|
||||
|
||||
/* public functions */
|
||||
int DDSGetInfo( ddsBuffer_t *dds, int *width, int *height, ddsPF_t *pf );
|
||||
int DDSDecompress( ddsBuffer_t *dds, unsigned char *pixels );
|
||||
|
||||
|
||||
|
||||
/* end marker */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
781
tools/urt/libs/ddslib/ddslib.c
Normal file
781
tools/urt/libs/ddslib/ddslib.c
Normal file
|
@ -0,0 +1,781 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
|
||||
DDS Library
|
||||
|
||||
Based on code from Nvidia's DDS example:
|
||||
http://www.nvidia.com/object/dxtc_decompression_code.html
|
||||
|
||||
Copyright (c) 2003 Randy Reddig
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
Neither the names of the copyright holders nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
|
||||
/* marker */
|
||||
#define DDSLIB_C
|
||||
|
||||
|
||||
|
||||
/* dependencies */
|
||||
#include "ddslib.h"
|
||||
|
||||
|
||||
|
||||
/* endian tomfoolery */
|
||||
typedef union
|
||||
{
|
||||
float f;
|
||||
char c[ 4 ];
|
||||
}
|
||||
floatSwapUnion;
|
||||
|
||||
|
||||
#ifndef __BIG_ENDIAN__
|
||||
#ifdef _SGI_SOURCE
|
||||
#define __BIG_ENDIAN__
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
|
||||
int DDSBigLong( int src ) { return src; }
|
||||
short DDSBigShort( short src ) { return src; }
|
||||
float DDSBigFloat( float src ) { return src; }
|
||||
|
||||
int DDSLittleLong( int src )
|
||||
{
|
||||
return ((src & 0xFF000000) >> 24) |
|
||||
((src & 0x00FF0000) >> 8) |
|
||||
((src & 0x0000FF00) << 8) |
|
||||
((src & 0x000000FF) << 24);
|
||||
}
|
||||
|
||||
short DDSLittleShort( short src )
|
||||
{
|
||||
return ((src & 0xFF00) >> 8) |
|
||||
((src & 0x00FF) << 8);
|
||||
}
|
||||
|
||||
float DDSLittleFloat( float src )
|
||||
{
|
||||
floatSwapUnion in,out;
|
||||
in.f = src;
|
||||
out.c[ 0 ] = in.c[ 3 ];
|
||||
out.c[ 1 ] = in.c[ 2 ];
|
||||
out.c[ 2 ] = in.c[ 1 ];
|
||||
out.c[ 3 ] = in.c[ 0 ];
|
||||
return out.f;
|
||||
}
|
||||
|
||||
#else /*__BIG_ENDIAN__*/
|
||||
|
||||
int DDSLittleLong( int src ) { return src; }
|
||||
short DDSLittleShort( short src ) { return src; }
|
||||
float DDSLittleFloat( float src ) { return src; }
|
||||
|
||||
int DDSBigLong( int src )
|
||||
{
|
||||
return ((src & 0xFF000000) >> 24) |
|
||||
((src & 0x00FF0000) >> 8) |
|
||||
((src & 0x0000FF00) << 8) |
|
||||
((src & 0x000000FF) << 24);
|
||||
}
|
||||
|
||||
short DDSBigShort( short src )
|
||||
{
|
||||
return ((src & 0xFF00) >> 8) |
|
||||
((src & 0x00FF) << 8);
|
||||
}
|
||||
|
||||
float DDSBigFloat( float src )
|
||||
{
|
||||
floatSwapUnion in,out;
|
||||
in.f = src;
|
||||
out.c[ 0 ] = in.c[ 3 ];
|
||||
out.c[ 1 ] = in.c[ 2 ];
|
||||
out.c[ 2 ] = in.c[ 1 ];
|
||||
out.c[ 3 ] = in.c[ 0 ];
|
||||
return out.f;
|
||||
}
|
||||
|
||||
#endif /*__BIG_ENDIAN__*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
DDSDecodePixelFormat()
|
||||
determines which pixel format the dds texture is in
|
||||
*/
|
||||
|
||||
static void DDSDecodePixelFormat( ddsBuffer_t *dds, ddsPF_t *pf )
|
||||
{
|
||||
unsigned int fourCC;
|
||||
|
||||
|
||||
/* dummy check */
|
||||
if( dds == NULL || pf == NULL )
|
||||
return;
|
||||
|
||||
/* extract fourCC */
|
||||
fourCC = dds->pixelFormat.fourCC;
|
||||
|
||||
/* test it */
|
||||
if( fourCC == 0 )
|
||||
*pf = DDS_PF_ARGB8888;
|
||||
else if( fourCC == *((unsigned int*) "DXT1") )
|
||||
*pf = DDS_PF_DXT1;
|
||||
else if( fourCC == *((unsigned int*) "DXT2") )
|
||||
*pf = DDS_PF_DXT2;
|
||||
else if( fourCC == *((unsigned int*) "DXT3") )
|
||||
*pf = DDS_PF_DXT3;
|
||||
else if( fourCC == *((unsigned int*) "DXT4") )
|
||||
*pf = DDS_PF_DXT4;
|
||||
else if( fourCC == *((unsigned int*) "DXT5") )
|
||||
*pf = DDS_PF_DXT5;
|
||||
else
|
||||
*pf = DDS_PF_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
DDSGetInfo()
|
||||
extracts relevant info from a dds texture, returns 0 on success
|
||||
*/
|
||||
|
||||
int DDSGetInfo( ddsBuffer_t *dds, int *width, int *height, ddsPF_t *pf )
|
||||
{
|
||||
/* dummy test */
|
||||
if( dds == NULL )
|
||||
return -1;
|
||||
|
||||
/* test dds header */
|
||||
if( *((int*) dds->magic) != *((int*) "DDS ") )
|
||||
return -1;
|
||||
if( DDSLittleLong( dds->size ) != 124 )
|
||||
return -1;
|
||||
|
||||
/* extract width and height */
|
||||
if( width != NULL )
|
||||
*width = DDSLittleLong( dds->width );
|
||||
if( height != NULL )
|
||||
*height = DDSLittleLong( dds->height );
|
||||
|
||||
/* get pixel format */
|
||||
DDSDecodePixelFormat( dds, pf );
|
||||
|
||||
/* return ok */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
DDSGetColorBlockColors()
|
||||
extracts colors from a dds color block
|
||||
*/
|
||||
|
||||
static void DDSGetColorBlockColors( ddsColorBlock_t *block, ddsColor_t colors[ 4 ] )
|
||||
{
|
||||
unsigned short word;
|
||||
|
||||
|
||||
/* color 0 */
|
||||
word = DDSLittleShort( block->colors[ 0 ] );
|
||||
colors[ 0 ].a = 0xff;
|
||||
|
||||
/* extract rgb bits */
|
||||
colors[ 0 ].b = (unsigned char) word;
|
||||
colors[ 0 ].b <<= 3;
|
||||
colors[ 0 ].b |= (colors[ 0 ].b >> 5);
|
||||
word >>= 5;
|
||||
colors[ 0 ].g = (unsigned char) word;
|
||||
colors[ 0 ].g <<= 2;
|
||||
colors[ 0 ].g |= (colors[ 0 ].g >> 5);
|
||||
word >>= 6;
|
||||
colors[ 0 ].r = (unsigned char) word;
|
||||
colors[ 0 ].r <<= 3;
|
||||
colors[ 0 ].r |= (colors[ 0 ].r >> 5);
|
||||
|
||||
/* same for color 1 */
|
||||
word = DDSLittleShort( block->colors[ 1 ] );
|
||||
colors[ 1 ].a = 0xff;
|
||||
|
||||
/* extract rgb bits */
|
||||
colors[ 1 ].b = (unsigned char) word;
|
||||
colors[ 1 ].b <<= 3;
|
||||
colors[ 1 ].b |= (colors[ 1 ].b >> 5);
|
||||
word >>= 5;
|
||||
colors[ 1 ].g = (unsigned char) word;
|
||||
colors[ 1 ].g <<= 2;
|
||||
colors[ 1 ].g |= (colors[ 1 ].g >> 5);
|
||||
word >>= 6;
|
||||
colors[ 1 ].r = (unsigned char) word;
|
||||
colors[ 1 ].r <<= 3;
|
||||
colors[ 1 ].r |= (colors[ 1 ].r >> 5);
|
||||
|
||||
/* use this for all but the super-freak math method */
|
||||
if( block->colors[ 0 ] > block->colors[ 1 ] )
|
||||
{
|
||||
/* four-color block: derive the other two colors.
|
||||
00 = color 0, 01 = color 1, 10 = color 2, 11 = color 3
|
||||
these two bit codes correspond to the 2-bit fields
|
||||
stored in the 64-bit block. */
|
||||
|
||||
word = ((unsigned short) colors[ 0 ].r * 2 + (unsigned short) colors[ 1 ].r ) / 3;
|
||||
/* no +1 for rounding */
|
||||
/* as bits have been shifted to 888 */
|
||||
colors[ 2 ].r = (unsigned char) word;
|
||||
word = ((unsigned short) colors[ 0 ].g * 2 + (unsigned short) colors[ 1 ].g) / 3;
|
||||
colors[ 2 ].g = (unsigned char) word;
|
||||
word = ((unsigned short) colors[ 0 ].b * 2 + (unsigned short) colors[ 1 ].b) / 3;
|
||||
colors[ 2 ].b = (unsigned char) word;
|
||||
colors[ 2 ].a = 0xff;
|
||||
|
||||
word = ((unsigned short) colors[ 0 ].r + (unsigned short) colors[ 1 ].r * 2) / 3;
|
||||
colors[ 3 ].r = (unsigned char) word;
|
||||
word = ((unsigned short) colors[ 0 ].g + (unsigned short) colors[ 1 ].g * 2) / 3;
|
||||
colors[ 3 ].g = (unsigned char) word;
|
||||
word = ((unsigned short) colors[ 0 ].b + (unsigned short) colors[ 1 ].b * 2) / 3;
|
||||
colors[ 3 ].b = (unsigned char) word;
|
||||
colors[ 3 ].a = 0xff;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* three-color block: derive the other color.
|
||||
00 = color 0, 01 = color 1, 10 = color 2,
|
||||
11 = transparent.
|
||||
These two bit codes correspond to the 2-bit fields
|
||||
stored in the 64-bit block */
|
||||
|
||||
word = ((unsigned short) colors[ 0 ].r + (unsigned short) colors[ 1 ].r) / 2;
|
||||
colors[ 2 ].r = (unsigned char) word;
|
||||
word = ((unsigned short) colors[ 0 ].g + (unsigned short) colors[ 1 ].g) / 2;
|
||||
colors[ 2 ].g = (unsigned char) word;
|
||||
word = ((unsigned short) colors[ 0 ].b + (unsigned short) colors[ 1 ].b) / 2;
|
||||
colors[ 2 ].b = (unsigned char) word;
|
||||
colors[ 2 ].a = 0xff;
|
||||
|
||||
/* random color to indicate alpha */
|
||||
colors[ 3 ].r = 0x00;
|
||||
colors[ 3 ].g = 0xff;
|
||||
colors[ 3 ].b = 0xff;
|
||||
colors[ 3 ].a = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
DDSDecodeColorBlock()
|
||||
decodes a dds color block
|
||||
fixme: make endian-safe
|
||||
*/
|
||||
|
||||
static void DDSDecodeColorBlock( unsigned int *pixel, ddsColorBlock_t *block, int width, unsigned int colors[ 4 ] )
|
||||
{
|
||||
int r, n;
|
||||
unsigned int bits;
|
||||
unsigned int masks[] = { 3, 12, 3 << 4, 3 << 6 }; /* bit masks = 00000011, 00001100, 00110000, 11000000 */
|
||||
int shift[] = { 0, 2, 4, 6 };
|
||||
|
||||
|
||||
/* r steps through lines in y */
|
||||
for( r = 0; r < 4; r++, pixel += (width - 4) ) /* no width * 4 as unsigned int ptr inc will * 4 */
|
||||
{
|
||||
/* width * 4 bytes per pixel per line, each j dxtc row is 4 lines of pixels */
|
||||
|
||||
/* n steps through pixels */
|
||||
for( n = 0; n < 4; n++ )
|
||||
{
|
||||
bits = block->row[ r ] & masks[ n ];
|
||||
bits >>= shift[ n ];
|
||||
|
||||
switch( bits )
|
||||
{
|
||||
case 0:
|
||||
*pixel = colors[ 0 ];
|
||||
pixel++;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
*pixel = colors[ 1 ];
|
||||
pixel++;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
*pixel = colors[ 2 ];
|
||||
pixel++;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
*pixel = colors[ 3 ];
|
||||
pixel++;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* invalid */
|
||||
pixel++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
DDSDecodeAlphaExplicit()
|
||||
decodes a dds explicit alpha block
|
||||
*/
|
||||
|
||||
static void DDSDecodeAlphaExplicit( unsigned int *pixel, ddsAlphaBlockExplicit_t *alphaBlock, int width, unsigned int alphaZero )
|
||||
{
|
||||
int row, pix;
|
||||
unsigned short word;
|
||||
ddsColor_t color;
|
||||
|
||||
|
||||
/* clear color */
|
||||
color.r = 0;
|
||||
color.g = 0;
|
||||
color.b = 0;
|
||||
|
||||
/* walk rows */
|
||||
for( row = 0; row < 4; row++, pixel += (width - 4) )
|
||||
{
|
||||
word = DDSLittleShort( alphaBlock->row[ row ] );
|
||||
|
||||
/* walk pixels */
|
||||
for( pix = 0; pix < 4; pix++ )
|
||||
{
|
||||
/* zero the alpha bits of image pixel */
|
||||
*pixel &= alphaZero;
|
||||
color.a = word & 0x000F;
|
||||
color.a = color.a | (color.a << 4);
|
||||
*pixel |= *((unsigned int*) &color);
|
||||
word >>= 4; /* move next bits to lowest 4 */
|
||||
pixel++; /* move to next pixel in the row */
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
DDSDecodeAlpha3BitLinear()
|
||||
decodes interpolated alpha block
|
||||
*/
|
||||
|
||||
static void DDSDecodeAlpha3BitLinear( unsigned int *pixel, ddsAlphaBlock3BitLinear_t *alphaBlock, int width, unsigned int alphaZero )
|
||||
{
|
||||
|
||||
int row, pix;
|
||||
unsigned int stuff;
|
||||
unsigned char bits[ 4 ][ 4 ];
|
||||
unsigned short alphas[ 8 ];
|
||||
ddsColor_t aColors[ 4 ][ 4 ];
|
||||
|
||||
|
||||
/* get initial alphas */
|
||||
alphas[ 0 ] = alphaBlock->alpha0;
|
||||
alphas[ 1 ] = alphaBlock->alpha1;
|
||||
|
||||
/* 8-alpha block */
|
||||
if( alphas[ 0 ] > alphas[ 1 ] )
|
||||
{
|
||||
/* 000 = alpha_0, 001 = alpha_1, others are interpolated */
|
||||
alphas[ 2 ] = ( 6 * alphas[ 0 ] + alphas[ 1 ]) / 7; /* bit code 010 */
|
||||
alphas[ 3 ] = ( 5 * alphas[ 0 ] + 2 * alphas[ 1 ]) / 7; /* bit code 011 */
|
||||
alphas[ 4 ] = ( 4 * alphas[ 0 ] + 3 * alphas[ 1 ]) / 7; /* bit code 100 */
|
||||
alphas[ 5 ] = ( 3 * alphas[ 0 ] + 4 * alphas[ 1 ]) / 7; /* bit code 101 */
|
||||
alphas[ 6 ] = ( 2 * alphas[ 0 ] + 5 * alphas[ 1 ]) / 7; /* bit code 110 */
|
||||
alphas[ 7 ] = ( alphas[ 0 ] + 6 * alphas[ 1 ]) / 7; /* bit code 111 */
|
||||
}
|
||||
|
||||
/* 6-alpha block */
|
||||
else
|
||||
{
|
||||
/* 000 = alpha_0, 001 = alpha_1, others are interpolated */
|
||||
alphas[ 2 ] = (4 * alphas[ 0 ] + alphas[ 1 ]) / 5; /* bit code 010 */
|
||||
alphas[ 3 ] = (3 * alphas[ 0 ] + 2 * alphas[ 1 ]) / 5; /* bit code 011 */
|
||||
alphas[ 4 ] = (2 * alphas[ 0 ] + 3 * alphas[ 1 ]) / 5; /* bit code 100 */
|
||||
alphas[ 5 ] = ( alphas[ 0 ] + 4 * alphas[ 1 ]) / 5; /* bit code 101 */
|
||||
alphas[ 6 ] = 0; /* bit code 110 */
|
||||
alphas[ 7 ] = 255; /* bit code 111 */
|
||||
}
|
||||
|
||||
/* decode 3-bit fields into array of 16 bytes with same value */
|
||||
|
||||
/* first two rows of 4 pixels each */
|
||||
stuff = *((unsigned int*) &(alphaBlock->stuff[ 0 ]));
|
||||
|
||||
bits[ 0 ][ 0 ] = (unsigned char) (stuff & 0x00000007);
|
||||
stuff >>= 3;
|
||||
bits[ 0 ][ 1 ] = (unsigned char) (stuff & 0x00000007);
|
||||
stuff >>= 3;
|
||||
bits[ 0 ][ 2 ] = (unsigned char) (stuff & 0x00000007);
|
||||
stuff >>= 3;
|
||||
bits[ 0 ][ 3 ] = (unsigned char) (stuff & 0x00000007);
|
||||
stuff >>= 3;
|
||||
bits[ 1 ][ 0 ] = (unsigned char) (stuff & 0x00000007);
|
||||
stuff >>= 3;
|
||||
bits[ 1 ][ 1 ] = (unsigned char) (stuff & 0x00000007);
|
||||
stuff >>= 3;
|
||||
bits[ 1 ][ 2 ] = (unsigned char) (stuff & 0x00000007);
|
||||
stuff >>= 3;
|
||||
bits[ 1 ][ 3 ] = (unsigned char) (stuff & 0x00000007);
|
||||
|
||||
/* last two rows */
|
||||
stuff = *((unsigned int*) &(alphaBlock->stuff[ 3 ])); /* last 3 bytes */
|
||||
|
||||
bits[ 2 ][ 0 ] = (unsigned char) (stuff & 0x00000007);
|
||||
stuff >>= 3;
|
||||
bits[ 2 ][ 1 ] = (unsigned char) (stuff & 0x00000007);
|
||||
stuff >>= 3;
|
||||
bits[ 2 ][ 2 ] = (unsigned char) (stuff & 0x00000007);
|
||||
stuff >>= 3;
|
||||
bits[ 2 ][ 3 ] = (unsigned char) (stuff & 0x00000007);
|
||||
stuff >>= 3;
|
||||
bits[ 3 ][ 0 ] = (unsigned char) (stuff & 0x00000007);
|
||||
stuff >>= 3;
|
||||
bits[ 3 ][ 1 ] = (unsigned char) (stuff & 0x00000007);
|
||||
stuff >>= 3;
|
||||
bits[ 3 ][ 2 ] = (unsigned char) (stuff & 0x00000007);
|
||||
stuff >>= 3;
|
||||
bits[ 3 ][ 3 ] = (unsigned char) (stuff & 0x00000007);
|
||||
|
||||
/* decode the codes into alpha values */
|
||||
for( row = 0; row < 4; row++ )
|
||||
{
|
||||
for( pix=0; pix < 4; pix++ )
|
||||
{
|
||||
aColors[ row ][ pix ].r = 0;
|
||||
aColors[ row ][ pix ].g = 0;
|
||||
aColors[ row ][ pix ].b = 0;
|
||||
aColors[ row ][ pix ].a = (unsigned char) alphas[ bits[ row ][ pix ] ];
|
||||
}
|
||||
}
|
||||
|
||||
/* write out alpha values to the image bits */
|
||||
for( row = 0; row < 4; row++, pixel += width-4 )
|
||||
{
|
||||
for( pix = 0; pix < 4; pix++ )
|
||||
{
|
||||
/* zero the alpha bits of image pixel */
|
||||
*pixel &= alphaZero;
|
||||
|
||||
/* or the bits into the prev. nulled alpha */
|
||||
*pixel |= *((unsigned int*) &(aColors[ row ][ pix ]));
|
||||
pixel++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
DDSDecompressDXT1()
|
||||
decompresses a dxt1 format texture
|
||||
*/
|
||||
|
||||
static int DDSDecompressDXT1( ddsBuffer_t *dds, int width, int height, unsigned char *pixels )
|
||||
{
|
||||
int x, y, xBlocks, yBlocks;
|
||||
unsigned int *pixel;
|
||||
ddsColorBlock_t *block;
|
||||
ddsColor_t colors[ 4 ];
|
||||
|
||||
|
||||
/* setup */
|
||||
xBlocks = width / 4;
|
||||
yBlocks = height / 4;
|
||||
|
||||
/* walk y */
|
||||
for( y = 0; y < yBlocks; y++ )
|
||||
{
|
||||
/* 8 bytes per block */
|
||||
block = (ddsColorBlock_t*) ((unsigned int) dds->data + y * xBlocks * 8);
|
||||
|
||||
/* walk x */
|
||||
for( x = 0; x < xBlocks; x++, block++ )
|
||||
{
|
||||
DDSGetColorBlockColors( block, colors );
|
||||
pixel = (unsigned int*) (pixels + x * 16 + (y * 4) * width * 4);
|
||||
DDSDecodeColorBlock( pixel, block, width, (unsigned int*) colors );
|
||||
}
|
||||
}
|
||||
|
||||
/* return ok */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
DDSDecompressDXT3()
|
||||
decompresses a dxt3 format texture
|
||||
*/
|
||||
|
||||
static int DDSDecompressDXT3( ddsBuffer_t *dds, int width, int height, unsigned char *pixels )
|
||||
{
|
||||
int x, y, xBlocks, yBlocks;
|
||||
unsigned int *pixel, alphaZero;
|
||||
ddsColorBlock_t *block;
|
||||
ddsAlphaBlockExplicit_t *alphaBlock;
|
||||
ddsColor_t colors[ 4 ];
|
||||
|
||||
|
||||
/* setup */
|
||||
xBlocks = width / 4;
|
||||
yBlocks = height / 4;
|
||||
|
||||
/* create zero alpha */
|
||||
colors[ 0 ].a = 0;
|
||||
colors[ 0 ].r = 0xFF;
|
||||
colors[ 0 ].g = 0xFF;
|
||||
colors[ 0 ].b = 0xFF;
|
||||
alphaZero = *((unsigned int*) &colors[ 0 ]);
|
||||
|
||||
/* walk y */
|
||||
for( y = 0; y < yBlocks; y++ )
|
||||
{
|
||||
/* 8 bytes per block, 1 block for alpha, 1 block for color */
|
||||
block = (ddsColorBlock_t*) ((unsigned int) dds->data + y * xBlocks * 16);
|
||||
|
||||
/* walk x */
|
||||
for( x = 0; x < xBlocks; x++, block++ )
|
||||
{
|
||||
/* get alpha block */
|
||||
alphaBlock = (ddsAlphaBlockExplicit_t*) block;
|
||||
|
||||
/* get color block */
|
||||
block++;
|
||||
DDSGetColorBlockColors( block, colors );
|
||||
|
||||
/* decode color block */
|
||||
pixel = (unsigned int*) (pixels + x * 16 + (y * 4) * width * 4);
|
||||
DDSDecodeColorBlock( pixel, block, width, (unsigned int*) colors );
|
||||
|
||||
/* overwrite alpha bits with alpha block */
|
||||
DDSDecodeAlphaExplicit( pixel, alphaBlock, width, alphaZero );
|
||||
}
|
||||
}
|
||||
|
||||
/* return ok */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
DDSDecompressDXT5()
|
||||
decompresses a dxt5 format texture
|
||||
*/
|
||||
|
||||
static int DDSDecompressDXT5( ddsBuffer_t *dds, int width, int height, unsigned char *pixels )
|
||||
{
|
||||
int x, y, xBlocks, yBlocks;
|
||||
unsigned int *pixel, alphaZero;
|
||||
ddsColorBlock_t *block;
|
||||
ddsAlphaBlock3BitLinear_t *alphaBlock;
|
||||
ddsColor_t colors[ 4 ];
|
||||
|
||||
|
||||
/* setup */
|
||||
xBlocks = width / 4;
|
||||
yBlocks = height / 4;
|
||||
|
||||
/* create zero alpha */
|
||||
colors[ 0 ].a = 0;
|
||||
colors[ 0 ].r = 0xFF;
|
||||
colors[ 0 ].g = 0xFF;
|
||||
colors[ 0 ].b = 0xFF;
|
||||
alphaZero = *((unsigned int*) &colors[ 0 ]);
|
||||
|
||||
/* walk y */
|
||||
for( y = 0; y < yBlocks; y++ )
|
||||
{
|
||||
/* 8 bytes per block, 1 block for alpha, 1 block for color */
|
||||
block = (ddsColorBlock_t*) ((unsigned int) dds->data + y * xBlocks * 16);
|
||||
|
||||
/* walk x */
|
||||
for( x = 0; x < xBlocks; x++, block++ )
|
||||
{
|
||||
/* get alpha block */
|
||||
alphaBlock = (ddsAlphaBlock3BitLinear_t*) block;
|
||||
|
||||
/* get color block */
|
||||
block++;
|
||||
DDSGetColorBlockColors( block, colors );
|
||||
|
||||
/* decode color block */
|
||||
pixel = (unsigned int*) (pixels + x * 16 + (y * 4) * width * 4);
|
||||
DDSDecodeColorBlock( pixel, block, width, (unsigned int*) colors );
|
||||
|
||||
/* overwrite alpha bits with alpha block */
|
||||
DDSDecodeAlpha3BitLinear( pixel, alphaBlock, width, alphaZero );
|
||||
}
|
||||
}
|
||||
|
||||
/* return ok */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
DDSDecompressDXT2()
|
||||
decompresses a dxt2 format texture (fixme: un-premultiply alpha)
|
||||
*/
|
||||
|
||||
static int DDSDecompressDXT2( ddsBuffer_t *dds, int width, int height, unsigned char *pixels )
|
||||
{
|
||||
int r;
|
||||
|
||||
|
||||
/* decompress dxt3 first */
|
||||
r = DDSDecompressDXT3( dds, width, height, pixels );
|
||||
|
||||
/* return to sender */
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
DDSDecompressDXT4()
|
||||
decompresses a dxt4 format texture (fixme: un-premultiply alpha)
|
||||
*/
|
||||
|
||||
static int DDSDecompressDXT4( ddsBuffer_t *dds, int width, int height, unsigned char *pixels )
|
||||
{
|
||||
int r;
|
||||
|
||||
|
||||
/* decompress dxt5 first */
|
||||
r = DDSDecompressDXT5( dds, width, height, pixels );
|
||||
|
||||
/* return to sender */
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
DDSDecompressARGB8888()
|
||||
decompresses an argb 8888 format texture
|
||||
*/
|
||||
|
||||
static int DDSDecompressARGB8888( ddsBuffer_t *dds, int width, int height, unsigned char *pixels )
|
||||
{
|
||||
int x, y;
|
||||
unsigned char *in, *out;
|
||||
|
||||
|
||||
/* setup */
|
||||
in = dds->data;
|
||||
out = pixels;
|
||||
|
||||
/* walk y */
|
||||
for( y = 0; y < height; y++ )
|
||||
{
|
||||
/* walk x */
|
||||
for( x = 0; x < width; x++ )
|
||||
{
|
||||
*out++ = *in++;
|
||||
*out++ = *in++;
|
||||
*out++ = *in++;
|
||||
*out++ = *in++;
|
||||
}
|
||||
}
|
||||
|
||||
/* return ok */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
DDSDecompress()
|
||||
decompresses a dds texture into an rgba image buffer, returns 0 on success
|
||||
*/
|
||||
|
||||
int DDSDecompress( ddsBuffer_t *dds, unsigned char *pixels )
|
||||
{
|
||||
int width, height, r;
|
||||
ddsPF_t pf;
|
||||
|
||||
|
||||
/* get dds info */
|
||||
r = DDSGetInfo( dds, &width, &height, &pf );
|
||||
if( r )
|
||||
return r;
|
||||
|
||||
/* decompress */
|
||||
switch( pf )
|
||||
{
|
||||
case DDS_PF_ARGB8888:
|
||||
/* fixme: support other [a]rgb formats */
|
||||
r = DDSDecompressARGB8888( dds, width, height, pixels );
|
||||
break;
|
||||
|
||||
case DDS_PF_DXT1:
|
||||
r = DDSDecompressDXT1( dds, width, height, pixels );
|
||||
break;
|
||||
|
||||
case DDS_PF_DXT2:
|
||||
r = DDSDecompressDXT2( dds, width, height, pixels );
|
||||
break;
|
||||
|
||||
case DDS_PF_DXT3:
|
||||
r = DDSDecompressDXT3( dds, width, height, pixels );
|
||||
break;
|
||||
|
||||
case DDS_PF_DXT4:
|
||||
r = DDSDecompressDXT4( dds, width, height, pixels );
|
||||
break;
|
||||
|
||||
case DDS_PF_DXT5:
|
||||
r = DDSDecompressDXT5( dds, width, height, pixels );
|
||||
break;
|
||||
|
||||
default:
|
||||
case DDS_PF_UNKNOWN:
|
||||
memset( pixels, 0xFF, width * height * 4 );
|
||||
r = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* return to sender */
|
||||
return r;
|
||||
}
|
||||
|
106
tools/urt/libs/ddslib/ddslib.dsp
Normal file
106
tools/urt/libs/ddslib/ddslib.dsp
Normal file
|
@ -0,0 +1,106 @@
|
|||
# Microsoft Developer Studio Project File - Name="ddslib" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Static Library" 0x0104
|
||||
|
||||
CFG=ddslib - Win32 Debug
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "ddslib.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "ddslib.mak" CFG="ddslib - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "ddslib - Win32 Release" (based on "Win32 (x86) Static Library")
|
||||
!MESSAGE "ddslib - Win32 Debug" (based on "Win32 (x86) Static Library")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName "Perforce Project"
|
||||
# PROP Scc_LocalPath ".."
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "ddslib - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Release"
|
||||
# PROP BASE Intermediate_Dir "Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Release"
|
||||
# PROP Intermediate_Dir "Release"
|
||||
# PROP Target_Dir ""
|
||||
F90=df.exe
|
||||
MTL=midl.exe
|
||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
|
||||
# ADD CPP /nologo /MD /W3 /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FR /FD /c
|
||||
# SUBTRACT CPP /YX
|
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LIB32=link.exe -lib
|
||||
# ADD BASE LIB32 /nologo
|
||||
# ADD LIB32 /nologo
|
||||
|
||||
!ELSEIF "$(CFG)" == "ddslib - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "Debug"
|
||||
# PROP BASE Intermediate_Dir "Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "Debug"
|
||||
# PROP Intermediate_Dir "Debug"
|
||||
# PROP Target_Dir ""
|
||||
F90=df.exe
|
||||
MTL=midl.exe
|
||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c
|
||||
# SUBTRACT CPP /YX
|
||||
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LIB32=link.exe -lib
|
||||
# ADD BASE LIB32 /nologo
|
||||
# ADD LIB32 /nologo
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "ddslib - Win32 Release"
|
||||
# Name "ddslib - Win32 Debug"
|
||||
# Begin Group "src"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ddslib.c
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "include"
|
||||
|
||||
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\ddslib.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# End Target
|
||||
# End Project
|
27
tools/urt/libs/ddslib/ddslib.plg
Normal file
27
tools/urt/libs/ddslib/ddslib.plg
Normal file
|
@ -0,0 +1,27 @@
|
|||
<html>
|
||||
<body>
|
||||
<pre>
|
||||
<h1>Build Log</h1>
|
||||
<h3>
|
||||
--------------------Configuration: ddslib - Win32 Debug--------------------
|
||||
</h3>
|
||||
<h3>Command Lines</h3>
|
||||
Creating temporary file "C:\DOCUME~1\TWENTY~1\LOCALS~1\Temp\RSP5F1.tmp" with contents
|
||||
[
|
||||
/nologo /MDd /W3 /Gm /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR"Debug/" /Fo"Debug/" /Fd"Debug/" /FD /GZ /c
|
||||
"C:\Program Files\Subversion\GtkRadiant\libs\ddslib\ddslib.c"
|
||||
]
|
||||
Creating command line "cl.exe @C:\DOCUME~1\TWENTY~1\LOCALS~1\Temp\RSP5F1.tmp"
|
||||
Creating command line "link.exe -lib /nologo /out:"Debug\ddslib.lib" ".\Debug\ddslib.obj" "
|
||||
<h3>Output Window</h3>
|
||||
Compiling...
|
||||
ddslib.c
|
||||
Creating library...
|
||||
|
||||
|
||||
|
||||
<h3>Results</h3>
|
||||
ddslib.lib - 0 error(s), 0 warning(s)
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
211
tools/urt/libs/ddslib/ddslib.vcproj
Normal file
211
tools/urt/libs/ddslib/ddslib.vcproj
Normal file
|
@ -0,0 +1,211 @@
|
|||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="8.00"
|
||||
Name="ddslib"
|
||||
ProjectGUID="{7FD1FB6D-9969-43E1-857D-1B70B85DB89D}"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory=".\Release"
|
||||
IntermediateDirectory=".\Release"
|
||||
ConfigurationType="4"
|
||||
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="false"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="2"
|
||||
InlineFunctionExpansion="1"
|
||||
AdditionalIncludeDirectories=".."
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
|
||||
StringPooling="true"
|
||||
RuntimeLibrary="2"
|
||||
EnableFunctionLevelLinking="true"
|
||||
PrecompiledHeaderFile=".\Release/ddslib.pch"
|
||||
AssemblerListingLocation=".\Release/"
|
||||
ObjectFile=".\Release/"
|
||||
ProgramDataBaseFileName=".\Release/"
|
||||
BrowseInformation="1"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="true"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="NDEBUG"
|
||||
Culture="1033"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
OutputFile=".\Release\ddslib.lib"
|
||||
SuppressStartupBanner="true"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
SuppressStartupBanner="true"
|
||||
OutputFile=".\Release/ddslib.bsc"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory=".\Debug"
|
||||
IntermediateDirectory=".\Debug"
|
||||
ConfigurationType="4"
|
||||
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="false"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories=".."
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="3"
|
||||
PrecompiledHeaderFile=".\Debug/ddslib.pch"
|
||||
AssemblerListingLocation=".\Debug/"
|
||||
ObjectFile=".\Debug/"
|
||||
ProgramDataBaseFileName=".\Debug/"
|
||||
BrowseInformation="1"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="true"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="_DEBUG"
|
||||
Culture="1033"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
OutputFile=".\Debug\ddslib.lib"
|
||||
SuppressStartupBanner="true"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
SuppressStartupBanner="true"
|
||||
OutputFile=".\Debug/ddslib.bsc"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="src"
|
||||
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||
>
|
||||
<File
|
||||
RelativePath="ddslib.c"
|
||||
>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalIncludeDirectories=""
|
||||
PreprocessorDefinitions=""
|
||||
/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalIncludeDirectories=""
|
||||
PreprocessorDefinitions=""
|
||||
/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="include"
|
||||
Filter="h;hpp;hxx;hm;inl"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\ddslib.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
8
tools/urt/libs/debugging/debugging.cpp
Normal file
8
tools/urt/libs/debugging/debugging.cpp
Normal file
|
@ -0,0 +1,8 @@
|
|||
|
||||
#include "debugging.h"
|
||||
|
||||
void TEST_ASSERT()
|
||||
{
|
||||
ERROR_MESSAGE("test");
|
||||
ASSERT_NOTNULL(0);
|
||||
}
|
115
tools/urt/libs/debugging/debugging.h
Normal file
115
tools/urt/libs/debugging/debugging.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
|
||||
#if !defined(INCLUDED_DEBUGGING_DEBUGGING_H)
|
||||
#define INCLUDED_DEBUGGING_DEBUGGING_H
|
||||
|
||||
/// \file
|
||||
/// \brief Debugging macros for fatal error/assert messages.
|
||||
|
||||
#include "stream/textstream.h"
|
||||
#include "warnings.h"
|
||||
#include "generic/static.h"
|
||||
|
||||
#if defined(_MSC_VER) && defined(_M_IX86)
|
||||
#define DEBUGGER_BREAKPOINT() __asm { int 3 }
|
||||
#elif defined (__i386__) && defined (__GNUC__) && __GNUC__ >= 2
|
||||
#define DEBUGGER_BREAKPOINT() __asm__ __volatile__ ("int $03")
|
||||
#else
|
||||
#include <signal.h>
|
||||
|
||||
#define DEBUGGER_BREAKPOINT() raise(SIGTRAP);
|
||||
#endif
|
||||
|
||||
|
||||
#define FILE_LINE __FILE__ ":" << __LINE__
|
||||
|
||||
#if defined(_DEBUG) || 1
|
||||
#define DEBUG_ASSERTS
|
||||
#endif
|
||||
|
||||
class DebugMessageHandler
|
||||
{
|
||||
public:
|
||||
virtual TextOutputStream& getOutputStream() = 0;
|
||||
virtual bool handleMessage() = 0;
|
||||
};
|
||||
|
||||
class NullDebugMessageHandler : public NullOutputStream, public DebugMessageHandler
|
||||
{
|
||||
public:
|
||||
virtual TextOutputStream& getOutputStream()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
virtual bool handleMessage()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class DefaultDebugMessageHandler : public DebugMessageHandler
|
||||
{
|
||||
public:
|
||||
virtual TextOutputStream& getOutputStream()
|
||||
{
|
||||
return globalErrorStream();
|
||||
}
|
||||
virtual bool handleMessage()
|
||||
{
|
||||
#if defined(_DEBUG)
|
||||
return false; // send debug-break
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
class DebugMessageHandlerRef : public DefaultDebugMessageHandler
|
||||
{
|
||||
DebugMessageHandler* m_handler;
|
||||
public:
|
||||
DebugMessageHandlerRef()
|
||||
: m_handler(this)
|
||||
{
|
||||
}
|
||||
void setHandler(DebugMessageHandler& handler)
|
||||
{
|
||||
m_handler = &handler;
|
||||
}
|
||||
DebugMessageHandler& getHandler()
|
||||
{
|
||||
return *m_handler;
|
||||
}
|
||||
};
|
||||
|
||||
typedef Static<DebugMessageHandlerRef> GlobalDebugMessageHandler;
|
||||
|
||||
inline DebugMessageHandler& globalDebugMessageHandler()
|
||||
{
|
||||
return GlobalDebugMessageHandler::instance().getHandler();
|
||||
}
|
||||
|
||||
#if defined(DEBUG_ASSERTS)
|
||||
|
||||
/// \brief Sends a \p message to the current debug-message-handler text-output-stream if \p condition evaluates to false.
|
||||
#define ASSERT_MESSAGE(condition, message)\
|
||||
if(!(condition))\
|
||||
{\
|
||||
globalDebugMessageHandler().getOutputStream() << FILE_LINE << "\nassertion failure: " << message << "\n";\
|
||||
if(!globalDebugMessageHandler().handleMessage()) { DEBUGGER_BREAKPOINT(); }\
|
||||
} else\
|
||||
|
||||
/// \brief Sends a \p message to the current debug-message-handler text-output-stream.
|
||||
#define ERROR_MESSAGE(message)\
|
||||
globalDebugMessageHandler().getOutputStream() << FILE_LINE << "\nruntime error: " << message << "\n";\
|
||||
if(!globalDebugMessageHandler().handleMessage()) { DEBUGGER_BREAKPOINT(); } else\
|
||||
|
||||
#define ASSERT_NOTNULL(ptr) ASSERT_MESSAGE(ptr != 0, "pointer \"" #ptr "\" is null")
|
||||
|
||||
#else
|
||||
|
||||
#define ASSERT_MESSAGE(condition, message)
|
||||
#define ASSERT_NOTNULL(ptr)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
3
tools/urt/libs/dragplanes.cpp
Normal file
3
tools/urt/libs/dragplanes.cpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
#include "dragplanes.h"
|
||||
|
233
tools/urt/libs/dragplanes.h
Normal file
233
tools/urt/libs/dragplanes.h
Normal file
|
@ -0,0 +1,233 @@
|
|||
|
||||
#if !defined(INCLUDED_DRAGPLANES_H)
|
||||
#define INCLUDED_DRAGPLANES_H
|
||||
|
||||
#include "selectable.h"
|
||||
#include "selectionlib.h"
|
||||
#include "math/aabb.h"
|
||||
#include "math/line.h"
|
||||
|
||||
class DragPlanes
|
||||
{
|
||||
public:
|
||||
ObservedSelectable m_selectable_right; // +x
|
||||
ObservedSelectable m_selectable_left; // -x
|
||||
ObservedSelectable m_selectable_front; // +y
|
||||
ObservedSelectable m_selectable_back; // -y
|
||||
ObservedSelectable m_selectable_top; // +z
|
||||
ObservedSelectable m_selectable_bottom; // -z
|
||||
Vector3 m_dragPlanesMin;
|
||||
Vector3 m_dragPlanesMax;
|
||||
Vector3 m_dragPlanesOrigin;
|
||||
Vector3 m_dragPlanesExtents;
|
||||
|
||||
DragPlanes(const SelectionChangeCallback& onchanged) :
|
||||
m_selectable_right(onchanged),
|
||||
m_selectable_left(onchanged),
|
||||
m_selectable_front(onchanged),
|
||||
m_selectable_back(onchanged),
|
||||
m_selectable_top(onchanged),
|
||||
m_selectable_bottom(onchanged)
|
||||
{
|
||||
}
|
||||
bool isSelected() const
|
||||
{
|
||||
return m_selectable_right.isSelected()
|
||||
|| m_selectable_left.isSelected()
|
||||
|| m_selectable_front.isSelected()
|
||||
|| m_selectable_back.isSelected()
|
||||
|| m_selectable_top.isSelected()
|
||||
|| m_selectable_bottom.isSelected();
|
||||
}
|
||||
void setSelected(bool selected)
|
||||
{
|
||||
m_selectable_right.setSelected(selected);
|
||||
m_selectable_left.setSelected(selected);
|
||||
m_selectable_front.setSelected(selected);
|
||||
m_selectable_back.setSelected(selected);
|
||||
m_selectable_top.setSelected(selected);
|
||||
m_selectable_bottom.setSelected(selected);
|
||||
}
|
||||
void selectPlanes(const AABB& aabb, Selector& selector, SelectionTest& test, const PlaneCallback& selectedPlaneCallback)
|
||||
{
|
||||
Line line(test.getNear(), test.getFar());
|
||||
Vector3 corners[8];
|
||||
aabb_corners(aabb, corners);
|
||||
Plane3 planes[6];
|
||||
aabb_planes(aabb, planes);
|
||||
|
||||
for(Vector3* i = corners; i != corners + 8; ++i)
|
||||
{
|
||||
*i = vector3_subtracted(line_closest_point(line, *i), *i);
|
||||
}
|
||||
|
||||
if(vector3_dot(planes[0].normal(), corners[1]) > 0
|
||||
&& vector3_dot(planes[0].normal(), corners[2]) > 0
|
||||
&& vector3_dot(planes[0].normal(), corners[5]) > 0
|
||||
&& vector3_dot(planes[0].normal(), corners[6]) > 0)
|
||||
{
|
||||
Selector_add(selector, m_selectable_right);
|
||||
selectedPlaneCallback(planes[0]);
|
||||
//globalOutputStream() << "right\n";
|
||||
}
|
||||
if(vector3_dot(planes[1].normal(), corners[0]) > 0
|
||||
&& vector3_dot(planes[1].normal(), corners[3]) > 0
|
||||
&& vector3_dot(planes[1].normal(), corners[4]) > 0
|
||||
&& vector3_dot(planes[1].normal(), corners[7]) > 0)
|
||||
{
|
||||
Selector_add(selector, m_selectable_left);
|
||||
selectedPlaneCallback(planes[1]);
|
||||
//globalOutputStream() << "left\n";
|
||||
}
|
||||
if(vector3_dot(planes[2].normal(), corners[0]) > 0
|
||||
&& vector3_dot(planes[2].normal(), corners[1]) > 0
|
||||
&& vector3_dot(planes[2].normal(), corners[4]) > 0
|
||||
&& vector3_dot(planes[2].normal(), corners[5]) > 0)
|
||||
{
|
||||
Selector_add(selector, m_selectable_front);
|
||||
selectedPlaneCallback(planes[2]);
|
||||
//globalOutputStream() << "front\n";
|
||||
}
|
||||
if(vector3_dot(planes[3].normal(), corners[2]) > 0
|
||||
&& vector3_dot(planes[3].normal(), corners[3]) > 0
|
||||
&& vector3_dot(planes[3].normal(), corners[6]) > 0
|
||||
&& vector3_dot(planes[3].normal(), corners[7]) > 0)
|
||||
{
|
||||
Selector_add(selector, m_selectable_back);
|
||||
selectedPlaneCallback(planes[3]);
|
||||
//globalOutputStream() << "back\n";
|
||||
}
|
||||
if(vector3_dot(planes[4].normal(), corners[0]) > 0
|
||||
&& vector3_dot(planes[4].normal(), corners[1]) > 0
|
||||
&& vector3_dot(planes[4].normal(), corners[2]) > 0
|
||||
&& vector3_dot(planes[4].normal(), corners[3]) > 0)
|
||||
{
|
||||
Selector_add(selector, m_selectable_top);
|
||||
selectedPlaneCallback(planes[4]);
|
||||
//globalOutputStream() << "top\n";
|
||||
}
|
||||
if(vector3_dot(planes[5].normal(), corners[4]) > 0
|
||||
&& vector3_dot(planes[5].normal(), corners[5]) > 0
|
||||
&& vector3_dot(planes[5].normal(), corners[6]) > 0
|
||||
&& vector3_dot(planes[5].normal(), corners[7]) > 0)
|
||||
{
|
||||
Selector_add(selector, m_selectable_bottom);
|
||||
//globalOutputStream() << "bottom\n";
|
||||
selectedPlaneCallback(planes[5]);
|
||||
}
|
||||
|
||||
m_dragPlanesMin = aabb.origin - aabb.extents;
|
||||
m_dragPlanesMax = aabb.origin + aabb.extents;
|
||||
m_dragPlanesOrigin = aabb.origin;
|
||||
m_dragPlanesExtents = aabb.extents;
|
||||
}
|
||||
void selectReversedPlanes(const AABB& aabb, Selector& selector, const SelectedPlanes& selectedPlanes)
|
||||
{
|
||||
Plane3 planes[6];
|
||||
aabb_planes(aabb, planes);
|
||||
|
||||
if(selectedPlanes.contains(plane3_flipped(planes[0])))
|
||||
{
|
||||
Selector_add(selector, m_selectable_right);
|
||||
}
|
||||
if(selectedPlanes.contains(plane3_flipped(planes[1])))
|
||||
{
|
||||
Selector_add(selector, m_selectable_left);
|
||||
}
|
||||
if(selectedPlanes.contains(plane3_flipped(planes[2])))
|
||||
{
|
||||
Selector_add(selector, m_selectable_front);
|
||||
}
|
||||
if(selectedPlanes.contains(plane3_flipped(planes[3])))
|
||||
{
|
||||
Selector_add(selector, m_selectable_back);
|
||||
}
|
||||
if(selectedPlanes.contains(plane3_flipped(planes[4])))
|
||||
{
|
||||
Selector_add(selector, m_selectable_top);
|
||||
}
|
||||
if(selectedPlanes.contains(plane3_flipped(planes[5])))
|
||||
{
|
||||
Selector_add(selector, m_selectable_bottom);
|
||||
}
|
||||
}
|
||||
void translate(const Vector3& translation)
|
||||
{
|
||||
if(m_dragPlanesExtents[0] != 0)
|
||||
{
|
||||
if(m_selectable_right.isSelected())
|
||||
{
|
||||
m_dragPlanesMax[0] += translation[0];
|
||||
//globalOutputStream() << "moving right\n";
|
||||
}
|
||||
if(m_selectable_left.isSelected())
|
||||
{
|
||||
m_dragPlanesMin[0] += translation[0];
|
||||
//globalOutputStream() << "moving left\n";
|
||||
}
|
||||
}
|
||||
if(m_dragPlanesExtents[1] != 0)
|
||||
{
|
||||
if(m_selectable_front.isSelected())
|
||||
{
|
||||
m_dragPlanesMax[1] += translation[1];
|
||||
//globalOutputStream() << "moving front\n";
|
||||
}
|
||||
if(m_selectable_back.isSelected())
|
||||
{
|
||||
m_dragPlanesMin[1] += translation[1];
|
||||
//globalOutputStream() << "moving back\n";
|
||||
}
|
||||
}
|
||||
if(m_dragPlanesExtents[2] != 0)
|
||||
{
|
||||
if(m_selectable_top.isSelected())
|
||||
{
|
||||
m_dragPlanesMax[2] += translation[2];
|
||||
//globalOutputStream() << "moving top\n";
|
||||
}
|
||||
if(m_selectable_bottom.isSelected())
|
||||
{
|
||||
m_dragPlanesMin[2] += translation[2];
|
||||
//globalOutputStream() << "moving bottom\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
Matrix4 evaluateTransform() const
|
||||
{
|
||||
Vector3 originTransformed(vector3_mid(m_dragPlanesMin, m_dragPlanesMax));
|
||||
Vector3 scale(vector3_scaled(vector3_subtracted(m_dragPlanesMax, m_dragPlanesMin), 0.5));
|
||||
|
||||
if(m_dragPlanesExtents[0] != 0)
|
||||
{
|
||||
scale[0] /= m_dragPlanesExtents[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
scale[0] = 1;
|
||||
}
|
||||
if(m_dragPlanesExtents[1] != 0)
|
||||
{
|
||||
scale[1] /= m_dragPlanesExtents[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
scale[1] = 1;
|
||||
}
|
||||
if(m_dragPlanesExtents[2] != 0)
|
||||
{
|
||||
scale[2] /= m_dragPlanesExtents[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
scale[2] = 1;
|
||||
}
|
||||
|
||||
Matrix4 matrix(matrix4_translation_for_vec3(originTransformed - m_dragPlanesOrigin));
|
||||
matrix4_pivoted_scale_by_vec3(matrix, scale, m_dragPlanesOrigin);
|
||||
|
||||
return matrix;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
2
tools/urt/libs/eclasslib.cpp
Normal file
2
tools/urt/libs/eclasslib.cpp
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include "eclasslib.h"
|
321
tools/urt/libs/eclasslib.h
Normal file
321
tools/urt/libs/eclasslib.h
Normal file
|
@ -0,0 +1,321 @@
|
|||
|
||||
#if !defined (INCLUDED_ECLASSLIB_H)
|
||||
#define INCLUDED_ECLASSLIB_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "ieclass.h"
|
||||
#include "irender.h"
|
||||
|
||||
#include "math/vector.h"
|
||||
#include "string/string.h"
|
||||
|
||||
typedef Vector3 Colour3;
|
||||
|
||||
class ListAttributeType
|
||||
{
|
||||
typedef std::pair<CopiedString, CopiedString> ListItem;
|
||||
typedef std::vector<ListItem> ListItems;
|
||||
ListItems m_items;
|
||||
public:
|
||||
|
||||
typedef ListItems::const_iterator const_iterator;
|
||||
const_iterator begin() const
|
||||
{
|
||||
return m_items.begin();
|
||||
}
|
||||
const_iterator end() const
|
||||
{
|
||||
return m_items.end();
|
||||
}
|
||||
|
||||
const ListItem& operator[](std::size_t i) const
|
||||
{
|
||||
return m_items[i];
|
||||
}
|
||||
const_iterator findValue(const char* value) const
|
||||
{
|
||||
for(ListItems::const_iterator i = m_items.begin(); i != m_items.end(); ++i)
|
||||
{
|
||||
if(string_equal(value, (*i).second.c_str()))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return m_items.end();
|
||||
}
|
||||
|
||||
void push_back(const char* name, const char* value)
|
||||
{
|
||||
m_items.push_back(ListItems::value_type(name, value));
|
||||
}
|
||||
};
|
||||
|
||||
class EntityClassAttribute
|
||||
{
|
||||
public:
|
||||
CopiedString m_type;
|
||||
CopiedString m_name;
|
||||
CopiedString m_value;
|
||||
CopiedString m_description;
|
||||
EntityClassAttribute()
|
||||
{
|
||||
}
|
||||
EntityClassAttribute(const char* type, const char* name, const char* value = "", const char* description = "") : m_type(type), m_name(name), m_value(value), m_description(description)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::pair<CopiedString, EntityClassAttribute> EntityClassAttributePair;
|
||||
typedef std::list<EntityClassAttributePair> EntityClassAttributes;
|
||||
typedef std::list<CopiedString> StringList;
|
||||
|
||||
inline const char* EntityClassAttributePair_getName(const EntityClassAttributePair& attributePair)
|
||||
{
|
||||
if(!string_empty(attributePair.second.m_name.c_str()))
|
||||
{
|
||||
return attributePair.second.m_name.c_str();
|
||||
}
|
||||
return attributePair.first.c_str();
|
||||
}
|
||||
|
||||
inline const char* EntityClassAttributePair_getDescription(const EntityClassAttributePair& attributePair)
|
||||
{
|
||||
if(!string_empty(attributePair.second.m_description.c_str()))
|
||||
{
|
||||
return attributePair.second.m_description.c_str();
|
||||
}
|
||||
return EntityClassAttributePair_getName(attributePair);
|
||||
}
|
||||
|
||||
class EntityClass
|
||||
{
|
||||
public:
|
||||
CopiedString m_name;
|
||||
StringList m_parent;
|
||||
bool fixedsize;
|
||||
bool unknown; // wasn't found in source
|
||||
Vector3 mins;
|
||||
Vector3 maxs;
|
||||
|
||||
Colour3 color;
|
||||
Shader* m_state_fill;
|
||||
Shader* m_state_wire;
|
||||
Shader* m_state_blend;
|
||||
|
||||
CopiedString m_comments;
|
||||
char flagnames[MAX_FLAGS][32];
|
||||
|
||||
CopiedString m_modelpath;
|
||||
CopiedString m_skin;
|
||||
|
||||
void (*free)(EntityClass*);
|
||||
|
||||
EntityClassAttributes m_attributes;
|
||||
|
||||
bool inheritanceResolved;
|
||||
bool sizeSpecified;
|
||||
bool colorSpecified;
|
||||
|
||||
const char* name() const
|
||||
{
|
||||
return m_name.c_str();
|
||||
}
|
||||
const char* comments() const
|
||||
{
|
||||
return m_comments.c_str();
|
||||
}
|
||||
const char* modelpath() const
|
||||
{
|
||||
return m_modelpath.c_str();
|
||||
}
|
||||
const char* skin() const
|
||||
{
|
||||
return m_skin.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
inline const char* EntityClass_valueForKey(const EntityClass& entityClass, const char* key)
|
||||
{
|
||||
for(EntityClassAttributes::const_iterator i = entityClass.m_attributes.begin(); i != entityClass.m_attributes.end(); ++i)
|
||||
{
|
||||
if(string_equal(key, (*i).first.c_str()))
|
||||
{
|
||||
return (*i).second.m_value.c_str();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
inline EntityClassAttributePair& EntityClass_insertAttribute(EntityClass& entityClass, const char* key, const EntityClassAttribute& attribute = EntityClassAttribute())
|
||||
{
|
||||
entityClass.m_attributes.push_back(EntityClassAttributePair(key, attribute));
|
||||
return entityClass.m_attributes.back();
|
||||
}
|
||||
|
||||
|
||||
inline void buffer_write_colour_fill(char buffer[128], const Colour3& colour)
|
||||
{
|
||||
sprintf(buffer, "(%g %g %g)", colour[0], colour[1], colour[2]);
|
||||
}
|
||||
|
||||
inline void buffer_write_colour_wire(char buffer[128], const Colour3& colour)
|
||||
{
|
||||
sprintf(buffer, "<%g %g %g>", colour[0], colour[1], colour[2]);
|
||||
}
|
||||
|
||||
inline void buffer_write_colour_blend(char buffer[128], const Colour3& colour)
|
||||
{
|
||||
sprintf(buffer, "[%g %g %g]", colour[0], colour[1], colour[2]);
|
||||
}
|
||||
|
||||
inline Shader* colour_capture_state_fill(const Colour3& colour)
|
||||
{
|
||||
char buffer[128];
|
||||
buffer_write_colour_fill(buffer, colour);
|
||||
return GlobalShaderCache().capture(buffer);
|
||||
}
|
||||
|
||||
inline void colour_release_state_fill(const Colour3& colour)
|
||||
{
|
||||
char buffer[128];
|
||||
buffer_write_colour_fill(buffer, colour);
|
||||
GlobalShaderCache().release(buffer);
|
||||
}
|
||||
|
||||
inline Shader* colour_capture_state_wire(const Colour3& colour)
|
||||
{
|
||||
char buffer[128];
|
||||
buffer_write_colour_wire(buffer, colour);
|
||||
return GlobalShaderCache().capture(buffer);
|
||||
}
|
||||
|
||||
inline void colour_release_state_wire(const Colour3& colour)
|
||||
{
|
||||
char buffer[128];
|
||||
buffer_write_colour_wire(buffer, colour);
|
||||
GlobalShaderCache().release(buffer);
|
||||
}
|
||||
|
||||
inline Shader* colour_capture_state_blend(const Colour3& colour)
|
||||
{
|
||||
char buffer[128];
|
||||
buffer_write_colour_blend(buffer, colour);
|
||||
return GlobalShaderCache().capture(buffer);
|
||||
}
|
||||
|
||||
inline void colour_release_state_blend(const Colour3& colour)
|
||||
{
|
||||
char buffer[128];
|
||||
buffer_write_colour_blend(buffer, colour);
|
||||
GlobalShaderCache().release(buffer);
|
||||
}
|
||||
|
||||
inline void eclass_capture_state(EntityClass* eclass)
|
||||
{
|
||||
eclass->m_state_fill = colour_capture_state_fill(eclass->color);
|
||||
eclass->m_state_wire = colour_capture_state_wire(eclass->color);
|
||||
eclass->m_state_blend = colour_capture_state_blend(eclass->color);
|
||||
}
|
||||
|
||||
inline void eclass_release_state(EntityClass* eclass)
|
||||
{
|
||||
colour_release_state_fill(eclass->color);
|
||||
colour_release_state_wire(eclass->color);
|
||||
colour_release_state_blend(eclass->color);
|
||||
}
|
||||
|
||||
// eclass constructor
|
||||
inline EntityClass* Eclass_Alloc()
|
||||
{
|
||||
EntityClass* e = new EntityClass;
|
||||
|
||||
e->fixedsize = false;
|
||||
e->unknown = false;
|
||||
memset(e->flagnames, 0, MAX_FLAGS*32);
|
||||
|
||||
e->maxs = Vector3(-1,-1,-1);
|
||||
e->mins = Vector3(1, 1, 1);
|
||||
|
||||
e->free = 0;
|
||||
|
||||
e->inheritanceResolved = true;
|
||||
e->sizeSpecified = false;
|
||||
e->colorSpecified = false;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
// eclass destructor
|
||||
inline void Eclass_Free(EntityClass* e)
|
||||
{
|
||||
eclass_release_state(e);
|
||||
|
||||
delete e;
|
||||
}
|
||||
|
||||
inline bool classname_equal(const char* classname, const char* other)
|
||||
{
|
||||
return string_equal(classname, other);
|
||||
}
|
||||
|
||||
inline EntityClass* EClass_Create(const char* name, const Vector3& colour, const char* comments)
|
||||
{
|
||||
EntityClass *e = Eclass_Alloc();
|
||||
e->free = &Eclass_Free;
|
||||
|
||||
e->m_name = name;
|
||||
|
||||
e->color = colour;
|
||||
eclass_capture_state(e);
|
||||
|
||||
if (comments)
|
||||
e->m_comments = comments;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
inline EntityClass* EClass_Create_FixedSize(const char* name, const Vector3& colour, const Vector3& mins, const Vector3& maxs, const char* comments)
|
||||
{
|
||||
EntityClass *e = Eclass_Alloc();
|
||||
e->free = &Eclass_Free;
|
||||
|
||||
e->m_name = name;
|
||||
|
||||
e->color = colour;
|
||||
eclass_capture_state(e);
|
||||
|
||||
e->fixedsize = true;
|
||||
|
||||
e->mins = mins;
|
||||
e->maxs = maxs;
|
||||
|
||||
if (comments)
|
||||
e->m_comments = comments;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
const Vector3 smallbox[2] = {
|
||||
Vector3(-8,-8,-8),
|
||||
Vector3( 8, 8, 8),
|
||||
};
|
||||
|
||||
inline EntityClass *EntityClass_Create_Default(const char *name, bool has_brushes)
|
||||
{
|
||||
// create a new class for it
|
||||
if (has_brushes)
|
||||
{
|
||||
return EClass_Create(name, Vector3(0.0f, 0.5f, 0.0f), "Not found in source.");
|
||||
}
|
||||
else
|
||||
{
|
||||
return EClass_Create_FixedSize(name, Vector3(0.0f, 0.5f, 0.0f), smallbox[0], smallbox[1], "Not found in source.");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
2
tools/urt/libs/entitylib.cpp
Normal file
2
tools/urt/libs/entitylib.cpp
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include "entitylib.h"
|
726
tools/urt/libs/entitylib.h
Normal file
726
tools/urt/libs/entitylib.h
Normal file
|
@ -0,0 +1,726 @@
|
|||
|
||||
#if !defined (INCLUDED_ENTITYLIB_H)
|
||||
#define INCLUDED_ENTITYLIB_H
|
||||
|
||||
#include "ireference.h"
|
||||
#include "debugging/debugging.h"
|
||||
|
||||
#include "ientity.h"
|
||||
#include "irender.h"
|
||||
#include "igl.h"
|
||||
#include "selectable.h"
|
||||
|
||||
#include "generic/callback.h"
|
||||
#include "math/vector.h"
|
||||
#include "math/aabb.h"
|
||||
#include "undolib.h"
|
||||
#include "string/string.h"
|
||||
#include "generic/referencecounted.h"
|
||||
#include "scenelib.h"
|
||||
#include "container/container.h"
|
||||
#include "eclasslib.h"
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
|
||||
inline void arrow_draw(const Vector3& origin, const Vector3& direction)
|
||||
{
|
||||
Vector3 up(0, 0, 1);
|
||||
Vector3 left(-direction[1], direction[0], 0);
|
||||
|
||||
Vector3 endpoint(vector3_added(origin, vector3_scaled(direction, 32.0)));
|
||||
|
||||
Vector3 tip1(vector3_added(vector3_added(endpoint, vector3_scaled(direction, -8.0)), vector3_scaled(up, -4.0)));
|
||||
Vector3 tip2(vector3_added(tip1, vector3_scaled(up, 8.0)));
|
||||
Vector3 tip3(vector3_added(vector3_added(endpoint, vector3_scaled(direction, -8.0)), vector3_scaled(left, -4.0)));
|
||||
Vector3 tip4(vector3_added(tip3, vector3_scaled(left, 8.0)));
|
||||
|
||||
glBegin (GL_LINES);
|
||||
|
||||
glVertex3fv(vector3_to_array(origin));
|
||||
glVertex3fv(vector3_to_array(endpoint));
|
||||
|
||||
glVertex3fv(vector3_to_array(endpoint));
|
||||
glVertex3fv(vector3_to_array(tip1));
|
||||
|
||||
glVertex3fv(vector3_to_array(endpoint));
|
||||
glVertex3fv(vector3_to_array(tip2));
|
||||
|
||||
glVertex3fv(vector3_to_array(endpoint));
|
||||
glVertex3fv(vector3_to_array(tip3));
|
||||
|
||||
glVertex3fv(vector3_to_array(endpoint));
|
||||
glVertex3fv(vector3_to_array(tip4));
|
||||
|
||||
glVertex3fv(vector3_to_array(tip1));
|
||||
glVertex3fv(vector3_to_array(tip3));
|
||||
|
||||
glVertex3fv(vector3_to_array(tip3));
|
||||
glVertex3fv(vector3_to_array(tip2));
|
||||
|
||||
glVertex3fv(vector3_to_array(tip2));
|
||||
glVertex3fv(vector3_to_array(tip4));
|
||||
|
||||
glVertex3fv(vector3_to_array(tip4));
|
||||
glVertex3fv(vector3_to_array(tip1));
|
||||
|
||||
glEnd();
|
||||
}
|
||||
|
||||
class SelectionIntersection;
|
||||
|
||||
inline void aabb_testselect(const AABB& aabb, SelectionTest& test, SelectionIntersection& best)
|
||||
{
|
||||
const IndexPointer::index_type indices[24] = {
|
||||
2, 1, 5, 6,
|
||||
1, 0, 4, 5,
|
||||
0, 1, 2, 3,
|
||||
3, 7, 4, 0,
|
||||
3, 2, 6, 7,
|
||||
7, 6, 5, 4,
|
||||
};
|
||||
|
||||
Vector3 points[8];
|
||||
aabb_corners(aabb, points);
|
||||
test.TestQuads(VertexPointer(reinterpret_cast<VertexPointer::pointer>(points), sizeof(Vector3)), IndexPointer(indices, 24), best);
|
||||
}
|
||||
|
||||
inline void aabb_draw_wire(const Vector3 points[8])
|
||||
{
|
||||
typedef std::size_t index_t;
|
||||
index_t indices[24] = {
|
||||
0, 1, 1, 2, 2, 3, 3, 0,
|
||||
4, 5, 5, 6, 6, 7, 7, 4,
|
||||
0, 4, 1, 5, 2, 6, 3, 7,
|
||||
};
|
||||
#if 1
|
||||
glVertexPointer(3, GL_FLOAT, 0, points);
|
||||
glDrawElements(GL_LINES, sizeof(indices)/sizeof(index_t), GL_UNSIGNED_INT, indices);
|
||||
#else
|
||||
glBegin(GL_LINES);
|
||||
for(std::size_t i = 0; i < sizeof(indices)/sizeof(index_t); ++i)
|
||||
{
|
||||
glVertex3fv(points[indices[i]]);
|
||||
}
|
||||
glEnd();
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void aabb_draw_flatshade(const Vector3 points[8])
|
||||
{
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
glNormal3fv(vector3_to_array(aabb_normals[0]));
|
||||
glVertex3fv(vector3_to_array(points[2]));
|
||||
glVertex3fv(vector3_to_array(points[1]));
|
||||
glVertex3fv(vector3_to_array(points[5]));
|
||||
glVertex3fv(vector3_to_array(points[6]));
|
||||
|
||||
glNormal3fv(vector3_to_array(aabb_normals[1]));
|
||||
glVertex3fv(vector3_to_array(points[1]));
|
||||
glVertex3fv(vector3_to_array(points[0]));
|
||||
glVertex3fv(vector3_to_array(points[4]));
|
||||
glVertex3fv(vector3_to_array(points[5]));
|
||||
|
||||
glNormal3fv(vector3_to_array(aabb_normals[2]));
|
||||
glVertex3fv(vector3_to_array(points[0]));
|
||||
glVertex3fv(vector3_to_array(points[1]));
|
||||
glVertex3fv(vector3_to_array(points[2]));
|
||||
glVertex3fv(vector3_to_array(points[3]));
|
||||
|
||||
glNormal3fv(vector3_to_array(aabb_normals[3]));
|
||||
glVertex3fv(vector3_to_array(points[0]));
|
||||
glVertex3fv(vector3_to_array(points[3]));
|
||||
glVertex3fv(vector3_to_array(points[7]));
|
||||
glVertex3fv(vector3_to_array(points[4]));
|
||||
|
||||
glNormal3fv(vector3_to_array(aabb_normals[4]));
|
||||
glVertex3fv(vector3_to_array(points[3]));
|
||||
glVertex3fv(vector3_to_array(points[2]));
|
||||
glVertex3fv(vector3_to_array(points[6]));
|
||||
glVertex3fv(vector3_to_array(points[7]));
|
||||
|
||||
glNormal3fv(vector3_to_array(aabb_normals[5]));
|
||||
glVertex3fv(vector3_to_array(points[7]));
|
||||
glVertex3fv(vector3_to_array(points[6]));
|
||||
glVertex3fv(vector3_to_array(points[5]));
|
||||
glVertex3fv(vector3_to_array(points[4]));
|
||||
|
||||
glEnd();
|
||||
}
|
||||
|
||||
inline void aabb_draw_wire(const AABB& aabb)
|
||||
{
|
||||
Vector3 points[8];
|
||||
aabb_corners(aabb, points);
|
||||
aabb_draw_wire(points);
|
||||
}
|
||||
|
||||
inline void aabb_draw_flatshade(const AABB& aabb)
|
||||
{
|
||||
Vector3 points[8];
|
||||
aabb_corners(aabb, points);
|
||||
aabb_draw_flatshade(points);
|
||||
}
|
||||
|
||||
inline void aabb_draw_textured(const AABB& aabb)
|
||||
{
|
||||
Vector3 points[8];
|
||||
aabb_corners(aabb, points);
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
glNormal3fv(vector3_to_array(aabb_normals[0]));
|
||||
glTexCoord2fv(aabb_texcoord_topleft);
|
||||
glVertex3fv(vector3_to_array(points[2]));
|
||||
glTexCoord2fv(aabb_texcoord_topright);
|
||||
glVertex3fv(vector3_to_array(points[1]));
|
||||
glTexCoord2fv(aabb_texcoord_botright);
|
||||
glVertex3fv(vector3_to_array(points[5]));
|
||||
glTexCoord2fv(aabb_texcoord_botleft);
|
||||
glVertex3fv(vector3_to_array(points[6]));
|
||||
|
||||
glNormal3fv(vector3_to_array(aabb_normals[1]));
|
||||
glTexCoord2fv(aabb_texcoord_topleft);
|
||||
glVertex3fv(vector3_to_array(points[1]));
|
||||
glTexCoord2fv(aabb_texcoord_topright);
|
||||
glVertex3fv(vector3_to_array(points[0]));
|
||||
glTexCoord2fv(aabb_texcoord_botright);
|
||||
glVertex3fv(vector3_to_array(points[4]));
|
||||
glTexCoord2fv(aabb_texcoord_botleft);
|
||||
glVertex3fv(vector3_to_array(points[5]));
|
||||
|
||||
glNormal3fv(vector3_to_array(aabb_normals[2]));
|
||||
glTexCoord2fv(aabb_texcoord_topleft);
|
||||
glVertex3fv(vector3_to_array(points[0]));
|
||||
glTexCoord2fv(aabb_texcoord_topright);
|
||||
glVertex3fv(vector3_to_array(points[1]));
|
||||
glTexCoord2fv(aabb_texcoord_botright);
|
||||
glVertex3fv(vector3_to_array(points[2]));
|
||||
glTexCoord2fv(aabb_texcoord_botleft);
|
||||
glVertex3fv(vector3_to_array(points[3]));
|
||||
|
||||
glNormal3fv(vector3_to_array(aabb_normals[3]));
|
||||
glTexCoord2fv(aabb_texcoord_topleft);
|
||||
glVertex3fv(vector3_to_array(points[0]));
|
||||
glTexCoord2fv(aabb_texcoord_topright);
|
||||
glVertex3fv(vector3_to_array(points[3]));
|
||||
glTexCoord2fv(aabb_texcoord_botright);
|
||||
glVertex3fv(vector3_to_array(points[7]));
|
||||
glTexCoord2fv(aabb_texcoord_botleft);
|
||||
glVertex3fv(vector3_to_array(points[4]));
|
||||
|
||||
glNormal3fv(vector3_to_array(aabb_normals[4]));
|
||||
glTexCoord2fv(aabb_texcoord_topleft);
|
||||
glVertex3fv(vector3_to_array(points[3]));
|
||||
glTexCoord2fv(aabb_texcoord_topright);
|
||||
glVertex3fv(vector3_to_array(points[2]));
|
||||
glTexCoord2fv(aabb_texcoord_botright);
|
||||
glVertex3fv(vector3_to_array(points[6]));
|
||||
glTexCoord2fv(aabb_texcoord_botleft);
|
||||
glVertex3fv(vector3_to_array(points[7]));
|
||||
|
||||
glNormal3fv(vector3_to_array(aabb_normals[5]));
|
||||
glTexCoord2fv(aabb_texcoord_topleft);
|
||||
glVertex3fv(vector3_to_array(points[7]));
|
||||
glTexCoord2fv(aabb_texcoord_topright);
|
||||
glVertex3fv(vector3_to_array(points[6]));
|
||||
glTexCoord2fv(aabb_texcoord_botright);
|
||||
glVertex3fv(vector3_to_array(points[5]));
|
||||
glTexCoord2fv(aabb_texcoord_botleft);
|
||||
glVertex3fv(vector3_to_array(points[4]));
|
||||
|
||||
glEnd();
|
||||
}
|
||||
|
||||
inline void aabb_draw_solid(const AABB& aabb, RenderStateFlags state)
|
||||
{
|
||||
if(state & RENDER_TEXTURE)
|
||||
{
|
||||
aabb_draw_textured(aabb);
|
||||
}
|
||||
else
|
||||
{
|
||||
aabb_draw_flatshade(aabb);
|
||||
}
|
||||
}
|
||||
|
||||
inline void aabb_draw(const AABB& aabb, RenderStateFlags state)
|
||||
{
|
||||
if(state & RENDER_FILL)
|
||||
{
|
||||
aabb_draw_solid(aabb, state);
|
||||
}
|
||||
else
|
||||
{
|
||||
aabb_draw_wire(aabb);
|
||||
}
|
||||
}
|
||||
|
||||
class RenderableSolidAABB : public OpenGLRenderable
|
||||
{
|
||||
const AABB& m_aabb;
|
||||
public:
|
||||
RenderableSolidAABB(const AABB& aabb) : m_aabb(aabb)
|
||||
{
|
||||
}
|
||||
void render(RenderStateFlags state) const
|
||||
{
|
||||
aabb_draw_solid(m_aabb, state);
|
||||
}
|
||||
};
|
||||
|
||||
class RenderableWireframeAABB : public OpenGLRenderable
|
||||
{
|
||||
const AABB& m_aabb;
|
||||
public:
|
||||
RenderableWireframeAABB(const AABB& aabb) : m_aabb(aabb)
|
||||
{
|
||||
}
|
||||
void render(RenderStateFlags state) const
|
||||
{
|
||||
aabb_draw_wire(m_aabb);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
typedef Callback1<const char*> KeyObserver;
|
||||
|
||||
/// \brief A key/value pair of strings.
|
||||
///
|
||||
/// - Notifies observers when value changes - value changes to "" on destruction.
|
||||
/// - Provides undo support through the global undo system.
|
||||
class KeyValue
|
||||
{
|
||||
typedef UnsortedSet<KeyObserver> KeyObservers;
|
||||
|
||||
std::size_t m_refcount;
|
||||
KeyObservers m_observers;
|
||||
CopiedString m_string;
|
||||
const char* m_empty;
|
||||
ObservedUndoableObject<CopiedString> m_undo;
|
||||
static EntityCreator::KeyValueChangedFunc m_entityKeyValueChanged;
|
||||
public:
|
||||
|
||||
KeyValue(const char* string, const char* empty)
|
||||
: m_refcount(0), m_string(string), m_empty(empty), m_undo(m_string, UndoImportCaller(*this))
|
||||
{
|
||||
notify();
|
||||
}
|
||||
~KeyValue()
|
||||
{
|
||||
ASSERT_MESSAGE(m_observers.empty(), "KeyValue::~KeyValue: observers still attached");
|
||||
}
|
||||
|
||||
static void setKeyValueChangedFunc(EntityCreator::KeyValueChangedFunc func)
|
||||
{
|
||||
m_entityKeyValueChanged = func;
|
||||
}
|
||||
|
||||
void IncRef()
|
||||
{
|
||||
++m_refcount;
|
||||
}
|
||||
void DecRef()
|
||||
{
|
||||
if(--m_refcount == 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
void instanceAttach(MapFile* map)
|
||||
{
|
||||
m_undo.instanceAttach(map);
|
||||
}
|
||||
void instanceDetach(MapFile* map)
|
||||
{
|
||||
m_undo.instanceDetach(map);
|
||||
}
|
||||
|
||||
void attach(const KeyObserver& observer)
|
||||
{
|
||||
(*m_observers.insert(observer))(c_str());
|
||||
}
|
||||
void detach(const KeyObserver& observer)
|
||||
{
|
||||
observer(m_empty);
|
||||
m_observers.erase(observer);
|
||||
}
|
||||
const char* c_str() const
|
||||
{
|
||||
if(string_empty(m_string.c_str()))
|
||||
{
|
||||
return m_empty;
|
||||
}
|
||||
return m_string.c_str();
|
||||
}
|
||||
void assign(const char* other)
|
||||
{
|
||||
if(!string_equal(m_string.c_str(), other))
|
||||
{
|
||||
m_undo.save();
|
||||
m_string = other;
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
void notify()
|
||||
{
|
||||
m_entityKeyValueChanged();
|
||||
KeyObservers::reverse_iterator i = m_observers.rbegin();
|
||||
while(i != m_observers.rend())
|
||||
{
|
||||
(*i++)(c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void importState(const CopiedString& string)
|
||||
{
|
||||
m_string = string;
|
||||
|
||||
notify();
|
||||
}
|
||||
typedef MemberCaller1<KeyValue, const CopiedString&, &KeyValue::importState> UndoImportCaller;
|
||||
};
|
||||
|
||||
/// \brief An unsorted list of key/value pairs.
|
||||
///
|
||||
/// - Notifies observers when a pair is inserted or removed.
|
||||
/// - Provides undo support through the global undo system.
|
||||
/// - New keys are appended to the end of the list.
|
||||
class EntityKeyValues : public Entity
|
||||
{
|
||||
public:
|
||||
typedef KeyValue Value;
|
||||
|
||||
class Observer
|
||||
{
|
||||
public:
|
||||
virtual void insert(const char* key, Value& value) = 0;
|
||||
virtual void erase(const char* key, Value& value) = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
static EntityCreator::KeyValueChangedFunc m_entityKeyValueChanged;
|
||||
static Counter* m_counter;
|
||||
|
||||
EntityClass* m_eclass;
|
||||
|
||||
typedef SmartPointer<KeyValue> KeyValuePtr;
|
||||
typedef UnsortedMap<CopiedString, KeyValuePtr > KeyValues;
|
||||
KeyValues m_keyValues;
|
||||
|
||||
typedef UnsortedSet<Observer*> Observers;
|
||||
Observers m_observers;
|
||||
|
||||
ObservedUndoableObject<KeyValues> m_undo;
|
||||
bool m_instanced;
|
||||
|
||||
bool m_observerMutex;
|
||||
|
||||
void notifyInsert(const char* key, Value& value)
|
||||
{
|
||||
m_observerMutex = true;
|
||||
for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i)
|
||||
{
|
||||
(*i)->insert(key, value);
|
||||
}
|
||||
m_observerMutex = false;
|
||||
}
|
||||
void notifyErase(const char* key, Value& value)
|
||||
{
|
||||
m_observerMutex = true;
|
||||
for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i)
|
||||
{
|
||||
(*i)->erase(key, value);
|
||||
}
|
||||
m_observerMutex = false;
|
||||
}
|
||||
void forEachKeyValue_notifyInsert()
|
||||
{
|
||||
for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
|
||||
{
|
||||
notifyInsert((*i).first.c_str(), *(*i).second);
|
||||
}
|
||||
}
|
||||
void forEachKeyValue_notifyErase()
|
||||
{
|
||||
for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
|
||||
{
|
||||
notifyErase((*i).first.c_str(), *(*i).second);
|
||||
}
|
||||
}
|
||||
|
||||
void insert(const char* key, const KeyValuePtr& keyValue)
|
||||
{
|
||||
KeyValues::iterator i = m_keyValues.insert(KeyValues::value_type(key, keyValue));
|
||||
notifyInsert(key, *(*i).second);
|
||||
|
||||
if(m_instanced)
|
||||
{
|
||||
(*i).second->instanceAttach(m_undo.map());
|
||||
}
|
||||
}
|
||||
|
||||
void insert(const char* key, const char* value)
|
||||
{
|
||||
KeyValues::iterator i = m_keyValues.find(key);
|
||||
if(i != m_keyValues.end())
|
||||
{
|
||||
(*i).second->assign(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_undo.save();
|
||||
insert(key, KeyValuePtr(new KeyValue(value, EntityClass_valueForKey(*m_eclass, key))));
|
||||
}
|
||||
}
|
||||
|
||||
void erase(KeyValues::iterator i)
|
||||
{
|
||||
if(m_instanced)
|
||||
{
|
||||
(*i).second->instanceDetach(m_undo.map());
|
||||
}
|
||||
|
||||
CopiedString key((*i).first);
|
||||
KeyValuePtr value((*i).second);
|
||||
m_keyValues.erase(i);
|
||||
notifyErase(key.c_str(), *value);
|
||||
}
|
||||
|
||||
void erase(const char* key)
|
||||
{
|
||||
KeyValues::iterator i = m_keyValues.find(key);
|
||||
if(i != m_keyValues.end())
|
||||
{
|
||||
m_undo.save();
|
||||
erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
EntityKeyValues(EntityClass* eclass) :
|
||||
m_eclass(eclass),
|
||||
m_undo(m_keyValues, UndoImportCaller(*this)),
|
||||
m_instanced(false),
|
||||
m_observerMutex(false)
|
||||
{
|
||||
}
|
||||
EntityKeyValues(const EntityKeyValues& other) :
|
||||
Entity(other),
|
||||
m_eclass(&other.getEntityClass()),
|
||||
m_undo(m_keyValues, UndoImportCaller(*this)),
|
||||
m_instanced(false),
|
||||
m_observerMutex(false)
|
||||
{
|
||||
for(KeyValues::const_iterator i = other.m_keyValues.begin(); i != other.m_keyValues.end(); ++i)
|
||||
{
|
||||
insert((*i).first.c_str(), (*i).second->c_str());
|
||||
}
|
||||
}
|
||||
~EntityKeyValues()
|
||||
{
|
||||
ASSERT_MESSAGE(m_observers.empty(), "EntityKeyValues::~EntityKeyValues: observers still attached");
|
||||
}
|
||||
|
||||
static void setKeyValueChangedFunc(EntityCreator::KeyValueChangedFunc func)
|
||||
{
|
||||
m_entityKeyValueChanged = func;
|
||||
KeyValue::setKeyValueChangedFunc(func);
|
||||
}
|
||||
static void setCounter(Counter* counter)
|
||||
{
|
||||
m_counter = counter;
|
||||
}
|
||||
|
||||
void importState(const KeyValues& keyValues)
|
||||
{
|
||||
for(KeyValues::iterator i = m_keyValues.begin(); i != m_keyValues.end();)
|
||||
{
|
||||
erase(i++);
|
||||
}
|
||||
|
||||
for(KeyValues::const_iterator i = keyValues.begin(); i != keyValues.end(); ++i)
|
||||
{
|
||||
insert((*i).first.c_str(), (*i).second);
|
||||
}
|
||||
|
||||
m_entityKeyValueChanged();
|
||||
}
|
||||
typedef MemberCaller1<EntityKeyValues, const KeyValues&, &EntityKeyValues::importState> UndoImportCaller;
|
||||
|
||||
void attach(Observer& observer)
|
||||
{
|
||||
ASSERT_MESSAGE(!m_observerMutex, "observer cannot be attached during iteration");
|
||||
m_observers.insert(&observer);
|
||||
for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
|
||||
{
|
||||
observer.insert((*i).first.c_str(), *(*i).second);
|
||||
}
|
||||
}
|
||||
void detach(Observer& observer)
|
||||
{
|
||||
ASSERT_MESSAGE(!m_observerMutex, "observer cannot be detached during iteration");
|
||||
m_observers.erase(&observer);
|
||||
for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
|
||||
{
|
||||
observer.erase((*i).first.c_str(), *(*i).second);
|
||||
}
|
||||
}
|
||||
|
||||
void forEachKeyValue_instanceAttach(MapFile* map)
|
||||
{
|
||||
for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
|
||||
{
|
||||
(*i).second->instanceAttach(map);
|
||||
}
|
||||
}
|
||||
void forEachKeyValue_instanceDetach(MapFile* map)
|
||||
{
|
||||
for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
|
||||
{
|
||||
(*i).second->instanceDetach(map);
|
||||
}
|
||||
}
|
||||
|
||||
void instanceAttach(MapFile* map)
|
||||
{
|
||||
if(m_counter != 0)
|
||||
{
|
||||
m_counter->increment();
|
||||
}
|
||||
|
||||
m_instanced = true;
|
||||
forEachKeyValue_instanceAttach(map);
|
||||
m_undo.instanceAttach(map);
|
||||
}
|
||||
void instanceDetach(MapFile* map)
|
||||
{
|
||||
if(m_counter != 0)
|
||||
{
|
||||
m_counter->decrement();
|
||||
}
|
||||
|
||||
m_undo.instanceDetach(map);
|
||||
forEachKeyValue_instanceDetach(map);
|
||||
m_instanced = false;
|
||||
}
|
||||
|
||||
// entity
|
||||
EntityClass& getEntityClass() const
|
||||
{
|
||||
return *m_eclass;
|
||||
}
|
||||
void forEachKeyValue(Visitor& visitor) const
|
||||
{
|
||||
for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i)
|
||||
{
|
||||
visitor.visit((*i).first.c_str(), (*i).second->c_str());
|
||||
}
|
||||
}
|
||||
void setKeyValue(const char* key, const char* value)
|
||||
{
|
||||
if(value[0] == '\0'
|
||||
/*|| string_equal(EntityClass_valueForKey(*m_eclass, key), value)*/) // don't delete values equal to default
|
||||
{
|
||||
erase(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
insert(key, value);
|
||||
}
|
||||
m_entityKeyValueChanged();
|
||||
}
|
||||
const char* getKeyValue(const char* key) const
|
||||
{
|
||||
KeyValues::const_iterator i = m_keyValues.find(key);
|
||||
if(i != m_keyValues.end())
|
||||
{
|
||||
return (*i).second->c_str();
|
||||
}
|
||||
|
||||
return EntityClass_valueForKey(*m_eclass, key);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief A Resource reference with a controlled lifetime.
|
||||
/// \brief The resource is released when the ResourceReference is destroyed.
|
||||
class ResourceReference
|
||||
{
|
||||
CopiedString m_name;
|
||||
Resource* m_resource;
|
||||
public:
|
||||
ResourceReference(const char* name)
|
||||
: m_name(name)
|
||||
{
|
||||
capture();
|
||||
}
|
||||
ResourceReference(const ResourceReference& other)
|
||||
: m_name(other.m_name)
|
||||
{
|
||||
capture();
|
||||
}
|
||||
ResourceReference& operator=(const ResourceReference& other)
|
||||
{
|
||||
ResourceReference tmp(other);
|
||||
tmp.swap(*this);
|
||||
return *this;
|
||||
}
|
||||
~ResourceReference()
|
||||
{
|
||||
release();
|
||||
}
|
||||
|
||||
void capture()
|
||||
{
|
||||
m_resource = GlobalReferenceCache().capture(m_name.c_str());
|
||||
}
|
||||
void release()
|
||||
{
|
||||
GlobalReferenceCache().release(m_name.c_str());
|
||||
}
|
||||
|
||||
const char* getName() const
|
||||
{
|
||||
return m_name.c_str();
|
||||
}
|
||||
void setName(const char* name)
|
||||
{
|
||||
ResourceReference tmp(name);
|
||||
tmp.swap(*this);
|
||||
}
|
||||
|
||||
void swap(ResourceReference& other)
|
||||
{
|
||||
std::swap(m_resource, other.m_resource);
|
||||
std::swap(m_name, other.m_name);
|
||||
}
|
||||
|
||||
void attach(ModuleObserver& observer)
|
||||
{
|
||||
m_resource->attach(observer);
|
||||
}
|
||||
void detach(ModuleObserver& observer)
|
||||
{
|
||||
m_resource->detach(observer);
|
||||
}
|
||||
|
||||
Resource* get()
|
||||
{
|
||||
return m_resource;
|
||||
}
|
||||
};
|
||||
|
||||
namespace std
|
||||
{
|
||||
/// \brief Swaps the values of \p self and \p other.
|
||||
/// Overloads std::swap.
|
||||
inline void swap(ResourceReference& self, ResourceReference& other)
|
||||
{
|
||||
self.swap(other);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
2
tools/urt/libs/entityxml.cpp
Normal file
2
tools/urt/libs/entityxml.cpp
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include "entityxml.h"
|
88
tools/urt/libs/entityxml.h
Normal file
88
tools/urt/libs/entityxml.h
Normal file
|
@ -0,0 +1,88 @@
|
|||
|
||||
#if !defined(INCLUDED_ENTITYXML_H)
|
||||
#define INCLUDED_ENTITYXML_H
|
||||
|
||||
#include "ientity.h"
|
||||
#include "xml/ixml.h"
|
||||
#include "xml/xmlelement.h"
|
||||
|
||||
class entity_import : public XMLImporter
|
||||
{
|
||||
Entity& m_entity;
|
||||
public:
|
||||
entity_import(Entity& entity)
|
||||
: m_entity(entity)
|
||||
{
|
||||
}
|
||||
void pushElement(const XMLElement& element)
|
||||
{
|
||||
if(strcmp(element.name(), "epair") == 0)
|
||||
m_entity.setKeyValue(element.attribute("key"), element.attribute("value"));
|
||||
}
|
||||
void popElement(const char* name)
|
||||
{
|
||||
}
|
||||
std::size_t write(const char* data, std::size_t length)
|
||||
{
|
||||
return length;
|
||||
}
|
||||
};
|
||||
|
||||
class entity_export : public XMLExporter
|
||||
{
|
||||
class ExportXMLVisitor : public Entity::Visitor
|
||||
{
|
||||
XMLImporter& m_importer;
|
||||
public:
|
||||
ExportXMLVisitor(XMLImporter& importer) : m_importer(importer)
|
||||
{
|
||||
}
|
||||
void visit(const char* key, const char* value)
|
||||
{
|
||||
StaticElement element("epair");
|
||||
element.insertAttribute("key", key);
|
||||
element.insertAttribute("value", value);
|
||||
m_importer.pushElement(element);
|
||||
m_importer.popElement(element.name());
|
||||
}
|
||||
};
|
||||
|
||||
const Entity& m_entity;
|
||||
|
||||
public:
|
||||
entity_export(const Entity& entity) : m_entity(entity)
|
||||
{
|
||||
}
|
||||
void exportXML(XMLImporter& observer)
|
||||
{
|
||||
ExportXMLVisitor visitor(observer);
|
||||
|
||||
m_entity.forEachKeyValue(visitor);
|
||||
}
|
||||
};
|
||||
|
||||
inline void entity_copy(Entity& entity, const Entity& other)
|
||||
{
|
||||
entity_export exporter(other);
|
||||
entity_import importer(entity);
|
||||
exporter.exportXML(importer);
|
||||
}
|
||||
|
||||
template<typename EntityType>
|
||||
class EntityConstruction
|
||||
{
|
||||
public:
|
||||
typedef EntityClass* type;
|
||||
static type get(const EntityType& entity)
|
||||
{
|
||||
return &entity.getEntity().getEntityClass();
|
||||
}
|
||||
static void copy(EntityType& entity, const EntityType& other)
|
||||
{
|
||||
entity_copy(entity.getEntity(), other.getEntity());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
2
tools/urt/libs/fs_filesystem.cpp
Normal file
2
tools/urt/libs/fs_filesystem.cpp
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include "fs_filesystem.h"
|
160
tools/urt/libs/fs_filesystem.h
Normal file
160
tools/urt/libs/fs_filesystem.h
Normal file
|
@ -0,0 +1,160 @@
|
|||
|
||||
#if !defined(INCLUDED_FS_FILESYSTEM_H)
|
||||
#define INCLUDED_FS_FILESYSTEM_H
|
||||
|
||||
#include "string/string.h"
|
||||
#include "os/path.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
inline unsigned int path_get_depth(const char* path)
|
||||
{
|
||||
unsigned int depth = 0;
|
||||
while(path != 0 && path[0] != '\0')
|
||||
{
|
||||
path = strchr(path, '/');
|
||||
if(path != 0)
|
||||
{
|
||||
++path;
|
||||
}
|
||||
++depth;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
/// \brief A generic unix-style file-system which maps paths to files and directories.
|
||||
/// Provides average O(log n) find and insert methods.
|
||||
/// \param file_type The data type which represents a file.
|
||||
template<typename file_type>
|
||||
class GenericFileSystem
|
||||
{
|
||||
class Path
|
||||
{
|
||||
CopiedString m_path;
|
||||
unsigned int m_depth;
|
||||
public:
|
||||
Path(const char* path)
|
||||
: m_path(path), m_depth(path_get_depth(c_str()))
|
||||
{
|
||||
}
|
||||
Path(const char* start, const char* finish)
|
||||
: m_path(start, finish), m_depth(path_get_depth(c_str()))
|
||||
{
|
||||
}
|
||||
bool operator<(const Path& other) const
|
||||
{
|
||||
return string_less_nocase(c_str(), other.c_str());
|
||||
}
|
||||
unsigned int depth() const
|
||||
{
|
||||
return m_depth;
|
||||
}
|
||||
const char* c_str() const
|
||||
{
|
||||
return m_path.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
class Entry
|
||||
{
|
||||
file_type* m_file;
|
||||
public:
|
||||
Entry(file_type* file)
|
||||
: m_file(file)
|
||||
{
|
||||
}
|
||||
file_type* file() const
|
||||
{
|
||||
return m_file;
|
||||
}
|
||||
bool is_directory() const
|
||||
{
|
||||
return file() == 0;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<Path, Entry> Entries;
|
||||
Entries m_entries;
|
||||
|
||||
public:
|
||||
typedef typename Entries::iterator iterator;
|
||||
typedef typename Entries::value_type value_type;
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
return m_entries.begin();
|
||||
}
|
||||
iterator end()
|
||||
{
|
||||
return m_entries.end();
|
||||
}
|
||||
|
||||
/// \brief Adds the file \p entry at \p path.
|
||||
/// Creates all directories below \p path if they do not exist.
|
||||
/// O(log n) on average.
|
||||
void insert(const Path& path, const Entry& entry)
|
||||
{
|
||||
{
|
||||
const char* end = path_remove_directory(path.c_str());
|
||||
while(end[0] != '\0')
|
||||
{
|
||||
Path dir(path.c_str(), end);
|
||||
m_entries.insert(value_type(dir, Entry(0)));
|
||||
end = path_remove_directory(end);
|
||||
}
|
||||
}
|
||||
|
||||
m_entries.insert(value_type(path, entry));
|
||||
}
|
||||
|
||||
/// \brief Returns the file at \p path or end() if not found.
|
||||
iterator find(const Path& path)
|
||||
{
|
||||
return m_entries.find(path);
|
||||
}
|
||||
|
||||
iterator begin(const char* root)
|
||||
{
|
||||
if(root[0] == '\0')
|
||||
{
|
||||
return m_entries.begin();
|
||||
}
|
||||
iterator i = m_entries.find(root);
|
||||
if(i == m_entries.end())
|
||||
{
|
||||
return i;
|
||||
}
|
||||
return ++i;
|
||||
}
|
||||
|
||||
/// \brief Performs a depth-first traversal of the file-system subtree rooted at \p root.
|
||||
/// Traverses the entire tree if \p root is "".
|
||||
/// Calls \p visitor.file() with the path to each file relative to the filesystem root.
|
||||
/// Calls \p visitor.directory() with the path to each directory relative to the filesystem root.
|
||||
template<typename visitor_type>
|
||||
void traverse(visitor_type visitor, const char* root)
|
||||
{
|
||||
unsigned int start_depth = path_get_depth(root);
|
||||
unsigned int skip_depth = 0;
|
||||
for(iterator i = begin(root); i != end() && i->first.depth() > start_depth; ++i)
|
||||
{
|
||||
if(i->first.depth() == skip_depth)
|
||||
{
|
||||
skip_depth = 0;
|
||||
}
|
||||
if(skip_depth == 0)
|
||||
{
|
||||
if(!i->second.is_directory())
|
||||
{
|
||||
visitor.file(i->first.c_str());
|
||||
}
|
||||
else if(visitor.directory(i->first.c_str(), i->first.depth() - start_depth))
|
||||
{
|
||||
skip_depth = i->first.depth();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
2
tools/urt/libs/fs_path.cpp
Normal file
2
tools/urt/libs/fs_path.cpp
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include "fs_path.h"
|
72
tools/urt/libs/fs_path.h
Normal file
72
tools/urt/libs/fs_path.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
|
||||
#if !defined(INCLUDED_FS_PATH_H)
|
||||
#define INCLUDED_FS_PATH_H
|
||||
|
||||
#include "stream/stringstream.h"
|
||||
|
||||
/// \brief A unix-style path string which can be modified at runtime.
|
||||
///
|
||||
/// - Maintains a path ending in a path-separator.
|
||||
/// - Provides a limited STL-style interface to push and pop file or directory names at the end of the path.
|
||||
class UnixPath
|
||||
{
|
||||
StringBuffer m_string;
|
||||
|
||||
void check_separator()
|
||||
{
|
||||
if(!empty() && m_string.back() != '/')
|
||||
{
|
||||
m_string.push_back('/');
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/// \brief Constructs with the directory \p root.
|
||||
UnixPath(const char* root)
|
||||
: m_string(root)
|
||||
{
|
||||
check_separator();
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return m_string.empty();
|
||||
}
|
||||
|
||||
const char* c_str() const
|
||||
{
|
||||
return m_string.c_str();
|
||||
}
|
||||
|
||||
/// \brief Appends the directory \p name.
|
||||
void push(const char* name)
|
||||
{
|
||||
m_string.push_string(name);
|
||||
check_separator();
|
||||
}
|
||||
/// \brief Appends the directory [\p first, \p last).
|
||||
void push(const char* first, const char* last)
|
||||
{
|
||||
m_string.push_range(first, last);
|
||||
check_separator();
|
||||
}
|
||||
/// \brief Appends the filename \p name.
|
||||
void push_filename(const char* name)
|
||||
{
|
||||
m_string.push_string(name);
|
||||
}
|
||||
/// \brief Removes the last directory or filename appended.
|
||||
void pop()
|
||||
{
|
||||
if(m_string.back() == '/')
|
||||
{
|
||||
m_string.pop_back();
|
||||
}
|
||||
while(!empty() && m_string.back() != '/')
|
||||
{
|
||||
m_string.pop_back();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
3
tools/urt/libs/generic/arrayrange.cpp
Normal file
3
tools/urt/libs/generic/arrayrange.cpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
#include "arrayrange.h"
|
||||
|
52
tools/urt/libs/generic/arrayrange.h
Normal file
52
tools/urt/libs/generic/arrayrange.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
|
||||
#if !defined(INCLUDED_GENERIC_ARRAYRANGE_H)
|
||||
#define INCLUDED_GENERIC_ARRAYRANGE_H
|
||||
|
||||
/// \file
|
||||
/// \brief Macros for automatically converting a compile-time-sized array to a range.
|
||||
|
||||
template<typename Element>
|
||||
struct ArrayRange
|
||||
{
|
||||
typedef Element* Iterator;
|
||||
ArrayRange(Iterator _begin, Iterator _end)
|
||||
: begin(_begin), end(_end)
|
||||
{
|
||||
}
|
||||
Iterator begin;
|
||||
Iterator end;
|
||||
};
|
||||
|
||||
template<typename Element>
|
||||
inline ArrayRange<Element> makeArrayRange(Element* begin, Element* end)
|
||||
{
|
||||
return ArrayRange<Element>(begin, end);
|
||||
}
|
||||
|
||||
template<typename Element>
|
||||
struct ArrayConstRange
|
||||
{
|
||||
typedef const Element* Iterator;
|
||||
ArrayConstRange(Iterator _begin, Iterator _end)
|
||||
: begin(_begin), end(_end)
|
||||
{
|
||||
}
|
||||
Iterator begin;
|
||||
Iterator end;
|
||||
};
|
||||
|
||||
template<typename Element>
|
||||
inline ArrayConstRange<Element> makeArrayRange(const Element* begin, const Element* end)
|
||||
{
|
||||
return ArrayConstRange<Element>(begin, end);
|
||||
}
|
||||
|
||||
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(*array))
|
||||
#define ARRAY_END(array) (array + ARRAY_SIZE(array))
|
||||
#define ARRAY_RANGE(array) (makeArrayRange(array, ARRAY_END(array)))
|
||||
|
||||
|
||||
typedef ArrayConstRange<const char*> StringArrayRange;
|
||||
#define STRING_ARRAY_RANGE(array) (StringArrayRange(array, ARRAY_END(array)))
|
||||
|
||||
#endif
|
3
tools/urt/libs/generic/bitfield.cpp
Normal file
3
tools/urt/libs/generic/bitfield.cpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
#include "bitfield.h"
|
||||
|
113
tools/urt/libs/generic/bitfield.h
Normal file
113
tools/urt/libs/generic/bitfield.h
Normal file
|
@ -0,0 +1,113 @@
|
|||
|
||||
#if !defined(INCLUDED_GENERIC_BITFIELD_H)
|
||||
#define INCLUDED_GENERIC_BITFIELD_H
|
||||
|
||||
/// \file
|
||||
/// \brief Type safe bitfield.
|
||||
|
||||
/// \brief A bit-field value.
|
||||
///
|
||||
/// - Can be forward-declared when the definition of Enumeration is unknown.
|
||||
/// - Can only be constructed from valid enumerated values.
|
||||
/// - Can only be compared and combined with others of the same type.
|
||||
///
|
||||
/// \param Enumeration A type that contains an enum \c Value of the bits that can be set in this field.
|
||||
template<typename Enumeration>
|
||||
class BitFieldValue : public Enumeration
|
||||
{
|
||||
unsigned m_value;
|
||||
protected:
|
||||
explicit BitFieldValue(unsigned value) : m_value(value)
|
||||
{
|
||||
}
|
||||
public:
|
||||
BitFieldValue() : m_value(0)
|
||||
{
|
||||
}
|
||||
explicit BitFieldValue(typename Enumeration::Value value) : m_value(1 << value)
|
||||
{
|
||||
}
|
||||
unsigned get() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Enumeration>
|
||||
class BitFieldValueUnsafe : public BitFieldValue<Enumeration>
|
||||
{
|
||||
public:
|
||||
explicit BitFieldValueUnsafe(unsigned value) : BitFieldValue<Enumeration>(value)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Enumeration>
|
||||
inline bool operator==(BitFieldValue<Enumeration> self, BitFieldValue<Enumeration> other)
|
||||
{
|
||||
return self.get() == other.get();
|
||||
}
|
||||
template<typename Enumeration>
|
||||
inline bool operator!=(BitFieldValue<Enumeration> self, BitFieldValue<Enumeration> other)
|
||||
{
|
||||
return !operator==(self, other);
|
||||
}
|
||||
|
||||
template<typename Enumeration>
|
||||
inline BitFieldValue<Enumeration> operator|(BitFieldValue<Enumeration> self, BitFieldValue<Enumeration> other)
|
||||
{
|
||||
return BitFieldValueUnsafe<Enumeration>(self.get() | other.get());
|
||||
}
|
||||
template<typename Enumeration>
|
||||
inline BitFieldValue<Enumeration>& operator|=(BitFieldValue<Enumeration>& self, BitFieldValue<Enumeration> other)
|
||||
{
|
||||
return self = self | other;
|
||||
}
|
||||
template<typename Enumeration>
|
||||
inline BitFieldValue<Enumeration> operator&(BitFieldValue<Enumeration> self, BitFieldValue<Enumeration> other)
|
||||
{
|
||||
return BitFieldValueUnsafe<Enumeration>(self.get() & other.get());
|
||||
}
|
||||
template<typename Enumeration>
|
||||
inline BitFieldValue<Enumeration>& operator&=(BitFieldValue<Enumeration>& self, BitFieldValue<Enumeration> other)
|
||||
{
|
||||
return self = self & other;
|
||||
}
|
||||
template<typename Enumeration>
|
||||
inline BitFieldValue<Enumeration> operator~(BitFieldValue<Enumeration> self)
|
||||
{
|
||||
return BitFieldValueUnsafe<Enumeration>(~self.get());
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline unsigned int bitfield_enable(unsigned int bitfield, unsigned int mask)
|
||||
{
|
||||
return bitfield | mask;
|
||||
}
|
||||
inline unsigned int bitfield_disable(unsigned int bitfield, unsigned int mask)
|
||||
{
|
||||
return bitfield & ~mask;
|
||||
}
|
||||
inline bool bitfield_enabled(unsigned int bitfield, unsigned int mask)
|
||||
{
|
||||
return (bitfield & mask) != 0;
|
||||
}
|
||||
|
||||
template<typename Enumeration>
|
||||
inline BitFieldValue<Enumeration> bitfield_enable(BitFieldValue<Enumeration> bitfield, BitFieldValue<Enumeration> mask)
|
||||
{
|
||||
return bitfield | mask;
|
||||
}
|
||||
template<typename Enumeration>
|
||||
inline BitFieldValue<Enumeration> bitfield_disable(BitFieldValue<Enumeration> bitfield, BitFieldValue<Enumeration> mask)
|
||||
{
|
||||
return bitfield & ~mask;
|
||||
}
|
||||
template<typename Enumeration>
|
||||
inline bool bitfield_enabled(BitFieldValue<Enumeration> bitfield, BitFieldValue<Enumeration> mask)
|
||||
{
|
||||
return (bitfield & mask).get() != 0;
|
||||
}
|
||||
|
||||
#endif
|
89
tools/urt/libs/generic/callback.cpp
Normal file
89
tools/urt/libs/generic/callback.cpp
Normal file
|
@ -0,0 +1,89 @@
|
|||
|
||||
#include "callback.h"
|
||||
|
||||
#if defined(_DEBUG) || defined(DOXYGEN)
|
||||
|
||||
namespace ExampleMemberCaller
|
||||
{
|
||||
// MemberCaller example
|
||||
class Integer
|
||||
{
|
||||
public:
|
||||
int value;
|
||||
|
||||
void printValue() const
|
||||
{
|
||||
// print this->value here;
|
||||
}
|
||||
|
||||
void setValue()
|
||||
{
|
||||
value = 3;
|
||||
}
|
||||
// a typedef to make things more readable
|
||||
typedef MemberCaller<Integer, &Integer::setValue> SetValueCaller;
|
||||
};
|
||||
|
||||
void example()
|
||||
{
|
||||
Integer foo = { 0 };
|
||||
|
||||
{
|
||||
Callback bar = ConstMemberCaller<Integer, &Integer::printValue>(foo);
|
||||
|
||||
// invoke the callback
|
||||
bar(); // foo.printValue()
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
// use the typedef to improve readability
|
||||
Callback bar = Integer::SetValueCaller(foo);
|
||||
|
||||
// invoke the callback
|
||||
bar(); // foo.setValue()
|
||||
}
|
||||
}
|
||||
// end example
|
||||
}
|
||||
|
||||
namespace ExampleReferenceCaller
|
||||
{
|
||||
// ReferenceCaller example
|
||||
void Int_printValue(const int& value)
|
||||
{
|
||||
// print value here;
|
||||
}
|
||||
|
||||
void Int_setValue(int& value)
|
||||
{
|
||||
value = 3;
|
||||
}
|
||||
|
||||
// a typedef to make things more readable
|
||||
typedef ReferenceCaller<int, Int_setValue> IntSetValueCaller;
|
||||
|
||||
void example()
|
||||
{
|
||||
int foo = 0;
|
||||
|
||||
{
|
||||
Callback bar = ConstReferenceCaller<int, Int_printValue>(foo);
|
||||
|
||||
// invoke the callback
|
||||
bar(); // Int_printValue(foo)
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
// use the typedef to improve readability
|
||||
Callback bar = IntSetValueCaller(foo);
|
||||
|
||||
// invoke the callback
|
||||
bar(); // Int_setValue(foo)
|
||||
}
|
||||
}
|
||||
// end example
|
||||
}
|
||||
|
||||
#endif
|
517
tools/urt/libs/generic/callback.h
Normal file
517
tools/urt/libs/generic/callback.h
Normal file
|
@ -0,0 +1,517 @@
|
|||
|
||||
#if !defined(INCLUDED_GENERIC_CLOSURE_H)
|
||||
#define INCLUDED_GENERIC_CLOSURE_H
|
||||
|
||||
/// \file
|
||||
/// \brief Type-safe techniques for binding the first argument of an anonymous callback.
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
/// \brief Combines a void pointer with a pointer to a function which operates on a void pointer.
|
||||
///
|
||||
/// Use with the callback constructors MemberCaller, ConstMemberCaller, ReferenceCaller, ConstReferenceCaller, PointerCaller, ConstPointerCaller and FreeCaller.
|
||||
class Callback
|
||||
{
|
||||
typedef void (*Thunk)(void*);
|
||||
void* m_environment;
|
||||
Thunk m_thunk;
|
||||
|
||||
static void nullThunk(void*)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
Callback() : m_environment(0), m_thunk(nullThunk)
|
||||
{
|
||||
}
|
||||
Callback(void* environment, Thunk function) : m_environment(environment), m_thunk(function)
|
||||
{
|
||||
}
|
||||
void* getEnvironment() const
|
||||
{
|
||||
return m_environment;
|
||||
}
|
||||
Thunk getThunk() const
|
||||
{
|
||||
return m_thunk;
|
||||
}
|
||||
void operator()() const
|
||||
{
|
||||
m_thunk(m_environment);
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator==(const Callback& self, const Callback& other)
|
||||
{
|
||||
return self.getEnvironment() == other.getEnvironment() && self.getThunk() == other.getThunk();
|
||||
}
|
||||
inline bool operator<(const Callback& self, const Callback& other)
|
||||
{
|
||||
return self.getEnvironment() < other.getEnvironment() ||
|
||||
(!(other.getEnvironment() < self.getEnvironment()) && self.getThunk() < other.getThunk());
|
||||
}
|
||||
|
||||
/// \brief Combines a void pointer with a pointer to a function which operates on a void pointer and one other argument.
|
||||
///
|
||||
/// Use with the callback constructors MemberCaller1, ConstMemberCaller1, ReferenceCaller1, ConstReferenceCaller1, PointerCaller1, ConstPointerCaller1 and FreeCaller1.
|
||||
template<typename FirstArgument>
|
||||
class Callback1
|
||||
{
|
||||
typedef void (*Thunk)(void*, FirstArgument);
|
||||
void* m_environment;
|
||||
Thunk m_thunk;
|
||||
|
||||
static void nullThunk(void*, FirstArgument)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
typedef FirstArgument first_argument_type;
|
||||
|
||||
Callback1() : m_environment(0), m_thunk(nullThunk)
|
||||
{
|
||||
}
|
||||
Callback1(void* environment, Thunk function) : m_environment(environment), m_thunk(function)
|
||||
{
|
||||
}
|
||||
void* getEnvironment() const
|
||||
{
|
||||
return m_environment;
|
||||
}
|
||||
Thunk getThunk() const
|
||||
{
|
||||
return m_thunk;
|
||||
}
|
||||
void operator()(FirstArgument firstArgument) const
|
||||
{
|
||||
m_thunk(m_environment, firstArgument);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename FirstArgument>
|
||||
inline bool operator==(const Callback1<FirstArgument>& self, const Callback1<FirstArgument>& other)
|
||||
{
|
||||
return self.getEnvironment() == other.getEnvironment() && self.getThunk() == other.getThunk();
|
||||
}
|
||||
template<typename FirstArgument>
|
||||
inline bool operator<(const Callback1<FirstArgument>& self, const Callback1<FirstArgument>& other)
|
||||
{
|
||||
return self.getEnvironment() < other.getEnvironment() ||
|
||||
(!(other.getEnvironment() < self.getEnvironment()) && self.getThunk() < other.getThunk());
|
||||
}
|
||||
|
||||
template<typename Functor>
|
||||
class FunctorInvoke
|
||||
{
|
||||
public:
|
||||
inline void operator()(Functor functor)
|
||||
{
|
||||
functor();
|
||||
}
|
||||
};
|
||||
|
||||
typedef FunctorInvoke<Callback> CallbackInvoke;
|
||||
|
||||
|
||||
template<typename Functor, typename FirstArgument>
|
||||
class Functor1Invoke
|
||||
{
|
||||
FirstArgument m_firstArgument;
|
||||
public:
|
||||
Functor1Invoke(FirstArgument firstArgument) : m_firstArgument(firstArgument)
|
||||
{
|
||||
}
|
||||
inline void operator()(Functor functor)
|
||||
{
|
||||
functor(m_firstArgument);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
typedef Callback1<bool> BoolImportCallback;
|
||||
typedef Callback1<const BoolImportCallback&> BoolExportCallback;
|
||||
|
||||
typedef Callback1<int> IntImportCallback;
|
||||
typedef Callback1<const IntImportCallback&> IntExportCallback;
|
||||
|
||||
typedef Callback1<float> FloatImportCallback;
|
||||
typedef Callback1<const FloatImportCallback&> FloatExportCallback;
|
||||
|
||||
typedef Callback1<const char*> StringImportCallback;
|
||||
typedef Callback1<const StringImportCallback&> StringExportCallback;
|
||||
|
||||
typedef Callback1<std::size_t> SizeImportCallback;
|
||||
typedef Callback1<const SizeImportCallback&> SizeExportCallback;
|
||||
|
||||
|
||||
/// \brief Forms a Callback from a non-const Environment reference and a non-const Environment member-function.
|
||||
///
|
||||
/// \dontinclude generic/callback.cpp
|
||||
/// \skipline MemberCaller example
|
||||
/// \until end example
|
||||
template<typename Environment, void (Environment::*member)()>
|
||||
class MemberCaller
|
||||
{
|
||||
Environment& m_environment;
|
||||
public:
|
||||
MemberCaller(Environment& environment) : m_environment(environment)
|
||||
{
|
||||
}
|
||||
void* getEnvironment() const
|
||||
{
|
||||
return &m_environment;
|
||||
}
|
||||
static void thunk(void* environment)
|
||||
{
|
||||
((*reinterpret_cast<Environment*>(environment)).*member)();
|
||||
}
|
||||
operator Callback() const
|
||||
{
|
||||
return Callback(getEnvironment(), thunk);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Forms a Callback from a const Environment reference and a const Environment member-function.
|
||||
///
|
||||
/// \dontinclude generic/callback.cpp
|
||||
/// \skipline MemberCaller example
|
||||
/// \until end example
|
||||
template<typename Environment, void (Environment::*member)() const>
|
||||
class ConstMemberCaller
|
||||
{
|
||||
const Environment& m_environment;
|
||||
public:
|
||||
ConstMemberCaller(const Environment& environment) : m_environment(environment)
|
||||
{
|
||||
}
|
||||
void* getEnvironment() const
|
||||
{
|
||||
return const_cast<Environment*>(&m_environment);
|
||||
}
|
||||
static void thunk(void* environment)
|
||||
{
|
||||
((*reinterpret_cast<const Environment*>(environment)).*member)();
|
||||
}
|
||||
operator Callback() const
|
||||
{
|
||||
return Callback(getEnvironment(), thunk);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Forms a Callback from a non-const Environment reference and a const Environment member-function which takes one argument.
|
||||
template<typename Environment, typename FirstArgument, void (Environment::*member)(FirstArgument)>
|
||||
class MemberCaller1
|
||||
{
|
||||
Environment& m_environment;
|
||||
public:
|
||||
MemberCaller1(Environment& environment) : m_environment(environment)
|
||||
{
|
||||
}
|
||||
void* getEnvironment() const
|
||||
{
|
||||
return &m_environment;
|
||||
}
|
||||
static void thunk(void* environment, FirstArgument firstArgument)
|
||||
{
|
||||
((*reinterpret_cast<Environment*>(environment)).*member)(firstArgument);
|
||||
}
|
||||
operator Callback1<FirstArgument>() const
|
||||
{
|
||||
return Callback1<FirstArgument>(getEnvironment(), thunk);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Forms a Callback from a const Environment reference and a const Environment member-function which takes one argument.
|
||||
template<typename Environment, typename FirstArgument, void (Environment::*member)(FirstArgument) const>
|
||||
class ConstMemberCaller1
|
||||
{
|
||||
const Environment& m_environment;
|
||||
public:
|
||||
ConstMemberCaller1(const Environment& environment) : m_environment(environment)
|
||||
{
|
||||
}
|
||||
void* getEnvironment() const
|
||||
{
|
||||
return const_cast<Environment*>(&m_environment);
|
||||
}
|
||||
static void thunk(void* environment, FirstArgument firstArgument)
|
||||
{
|
||||
((*reinterpret_cast<Environment*>(environment)).*member)(firstArgument);
|
||||
}
|
||||
operator Callback1<FirstArgument>() const
|
||||
{
|
||||
return Callback1<FirstArgument>(getEnvironment(), thunk);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Forms a Callback from a non-const Environment reference and a free function which operates on a non-const Environment reference.
|
||||
///
|
||||
/// \dontinclude generic/callback.cpp
|
||||
/// \skipline ReferenceCaller example
|
||||
/// \until end example
|
||||
template<typename Environment, void (*func)(Environment&)>
|
||||
class ReferenceCaller
|
||||
{
|
||||
Environment& m_environment;
|
||||
public:
|
||||
ReferenceCaller(Environment& environment) : m_environment(environment)
|
||||
{
|
||||
}
|
||||
void* getEnvironment() const
|
||||
{
|
||||
return &m_environment;
|
||||
}
|
||||
static void thunk(void* environment)
|
||||
{
|
||||
(func)(*reinterpret_cast<Environment*>(environment));
|
||||
}
|
||||
operator Callback() const
|
||||
{
|
||||
return Callback(getEnvironment(), thunk);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Forms a Callback from a const Environment reference and a free function which operates on a const Environment reference.
|
||||
///
|
||||
/// \dontinclude generic/callback.cpp
|
||||
/// \skipline ReferenceCaller example
|
||||
/// \until end example
|
||||
template<typename Environment, void (*func)(const Environment&)>
|
||||
class ConstReferenceCaller
|
||||
{
|
||||
const Environment& m_environment;
|
||||
public:
|
||||
ConstReferenceCaller(const Environment& environment) : m_environment(environment)
|
||||
{
|
||||
}
|
||||
void* getEnvironment() const
|
||||
{
|
||||
return const_cast<Environment*>(&m_environment);
|
||||
}
|
||||
static void thunk(void* environment)
|
||||
{
|
||||
(func)(*reinterpret_cast<const Environment*>(environment));
|
||||
}
|
||||
operator Callback() const
|
||||
{
|
||||
return Callback(getEnvironment(), thunk);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Forms a Callback from a non-const Environment reference and a free function which operates on a non-const Environment reference and one other argument.
|
||||
template<typename Environment, typename FirstArgument, void (*func)(Environment&, FirstArgument)>
|
||||
class ReferenceCaller1
|
||||
{
|
||||
Environment& m_environment;
|
||||
public:
|
||||
ReferenceCaller1(Environment& environment) : m_environment(environment)
|
||||
{
|
||||
}
|
||||
void* getEnvironment() const
|
||||
{
|
||||
return &m_environment;
|
||||
}
|
||||
static void thunk(void* environment, FirstArgument firstArgument)
|
||||
{
|
||||
(func)(*reinterpret_cast<Environment*>(environment), firstArgument);
|
||||
}
|
||||
operator Callback1<FirstArgument>() const
|
||||
{
|
||||
return Callback1<FirstArgument>(getEnvironment(), thunk);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Forms a Callback from a const Environment reference and a free function which operates on a const Environment reference and one other argument.
|
||||
template<typename Environment, typename FirstArgument, void (*func)(const Environment&, FirstArgument)>
|
||||
class ConstReferenceCaller1
|
||||
{
|
||||
const Environment& m_environment;
|
||||
public:
|
||||
ConstReferenceCaller1(const Environment& environment) : m_environment(environment)
|
||||
{
|
||||
}
|
||||
void* getEnvironment() const
|
||||
{
|
||||
return const_cast<Environment*>(&m_environment);
|
||||
}
|
||||
static void thunk(void* environment, FirstArgument firstArgument)
|
||||
{
|
||||
(func)(*reinterpret_cast<const Environment*>(environment), firstArgument);
|
||||
}
|
||||
operator Callback1<FirstArgument>() const
|
||||
{
|
||||
return Callback1<FirstArgument>(getEnvironment(), thunk);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Forms a Callback from a non-const Environment pointer and a free function which operates on a non-const Environment pointer.
|
||||
template<typename Environment, void (*func)(Environment*)>
|
||||
class PointerCaller
|
||||
{
|
||||
Environment* m_environment;
|
||||
public:
|
||||
PointerCaller(Environment* environment) : m_environment(environment)
|
||||
{
|
||||
}
|
||||
void* getEnvironment() const
|
||||
{
|
||||
return m_environment;
|
||||
}
|
||||
static void thunk(void* environment)
|
||||
{
|
||||
(func)(reinterpret_cast<Environment*>(environment));
|
||||
}
|
||||
operator Callback() const
|
||||
{
|
||||
return Callback(getEnvironment(), thunk);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Forms a Callback from a const Environment pointer and a free function which operates on a const Environment pointer.
|
||||
template<typename Environment, void (*func)(const Environment*)>
|
||||
class ConstPointerCaller
|
||||
{
|
||||
const Environment* m_environment;
|
||||
public:
|
||||
ConstPointerCaller(const Environment* environment) : m_environment(environment)
|
||||
{
|
||||
}
|
||||
void* getEnvironment() const
|
||||
{
|
||||
return const_cast<Environment*>(m_environment);
|
||||
}
|
||||
static void thunk(void* environment)
|
||||
{
|
||||
(func)(reinterpret_cast<const Environment*>(environment));
|
||||
}
|
||||
operator Callback() const
|
||||
{
|
||||
return Callback(getEnvironment(), thunk);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Forms a Callback from a non-const Environment pointer and a free function which operates on a non-const Environment pointer and one other argument.
|
||||
template<typename Environment, typename FirstArgument, void (*func)(Environment*, FirstArgument)>
|
||||
class PointerCaller1
|
||||
{
|
||||
Environment* m_environment;
|
||||
public:
|
||||
PointerCaller1(Environment* environment) : m_environment(environment)
|
||||
{
|
||||
}
|
||||
void* getEnvironment() const
|
||||
{
|
||||
return m_environment;
|
||||
}
|
||||
static void thunk(void* environment, FirstArgument firstArgument)
|
||||
{
|
||||
(func)(reinterpret_cast<Environment*>(environment), firstArgument);
|
||||
}
|
||||
operator Callback1<FirstArgument>() const
|
||||
{
|
||||
return Callback1<FirstArgument>(getEnvironment(), thunk);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Forms a Callback from a const Environment pointer and a free function which operates on a const Environment pointer and one other argument.
|
||||
template<typename Environment, typename FirstArgument, void (*func)(const Environment*, FirstArgument)>
|
||||
class ConstPointerCaller1
|
||||
{
|
||||
const Environment* m_environment;
|
||||
public:
|
||||
ConstPointerCaller1(const Environment* environment) : m_environment(environment)
|
||||
{
|
||||
}
|
||||
void* getEnvironment() const
|
||||
{
|
||||
return const_cast<Environment*>(m_environment);
|
||||
}
|
||||
static void thunk(void* environment, FirstArgument firstArgument)
|
||||
{
|
||||
(func)(reinterpret_cast<const Environment*>(environment), firstArgument);
|
||||
}
|
||||
operator Callback1<FirstArgument>() const
|
||||
{
|
||||
return Callback1<FirstArgument>(getEnvironment(), thunk);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// \brief Forms a Callback from a free function which takes no arguments.
|
||||
template<void (*func)()>
|
||||
class FreeCaller
|
||||
{
|
||||
public:
|
||||
void* getEnvironment() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static void thunk(void*)
|
||||
{
|
||||
(func)();
|
||||
}
|
||||
operator Callback() const
|
||||
{
|
||||
return Callback(getEnvironment(), thunk);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Forms a Callback from a free function which takes a single argument.
|
||||
template<typename FirstArgument, void (*func)(FirstArgument)>
|
||||
class FreeCaller1
|
||||
{
|
||||
public:
|
||||
void* getEnvironment() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static void thunk(void*, FirstArgument firstArgument)
|
||||
{
|
||||
(func)(firstArgument);
|
||||
}
|
||||
operator Callback1<FirstArgument>() const
|
||||
{
|
||||
return Callback1<FirstArgument>(getEnvironment(), thunk);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// \brief Constructs a Callback from a non-const \p functor with zero arguments.
|
||||
///
|
||||
/// \param Functor Must define \c operator()().
|
||||
template<typename Functor>
|
||||
inline Callback makeCallback(Functor& functor)
|
||||
{
|
||||
return Callback(MemberCaller<Functor, &Functor::operator()>(functor));
|
||||
}
|
||||
|
||||
/// \brief Constructs a Callback from a const \p functor with zero arguments.
|
||||
///
|
||||
/// \param Functor Must define const \c operator()().
|
||||
template<typename Functor>
|
||||
inline Callback makeCallback(const Functor& functor)
|
||||
{
|
||||
return Callback(ConstMemberCaller<Functor, &Functor::operator()>(functor));
|
||||
}
|
||||
|
||||
/// \brief Constructs a Callback1 from a non-const \p functor with one argument.
|
||||
///
|
||||
/// \param Functor Must define \c first_argument_type and \c operator()(first_argument_type).
|
||||
template<typename Functor>
|
||||
inline Callback1<typename Functor::first_argument_type> makeCallback1(Functor& functor)
|
||||
{
|
||||
typedef typename Functor::first_argument_type FirstArgument;
|
||||
return Callback1<FirstArgument>(MemberCaller1<Functor, FirstArgument, &Functor::operator()>(functor));
|
||||
}
|
||||
|
||||
/// \brief Constructs a Callback1 from a const \p functor with one argument.
|
||||
///
|
||||
/// \param Functor Must define \c first_argument_type and const \c operator()(first_argument_type).
|
||||
template<typename Functor>
|
||||
inline Callback1<typename Functor::first_argument_type> makeCallback1(const Functor& functor)
|
||||
{
|
||||
typedef typename Functor::first_argument_type FirstArgument;
|
||||
return Callback1<FirstArgument>(ConstMemberCaller1<Functor, FirstArgument, &Functor::operator()>(functor));
|
||||
}
|
||||
|
||||
#endif
|
3
tools/urt/libs/generic/enumeration.cpp
Normal file
3
tools/urt/libs/generic/enumeration.cpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
#include "enumeration.h"
|
||||
|
40
tools/urt/libs/generic/enumeration.h
Normal file
40
tools/urt/libs/generic/enumeration.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
|
||||
#if !defined(INCLUDED_GENERIC_ENUMERATION_H)
|
||||
#define INCLUDED_GENERIC_ENUMERATION_H
|
||||
|
||||
/// \file
|
||||
/// \brief Type safe enumeration.
|
||||
|
||||
/// \brief An enumerated value.
|
||||
///
|
||||
/// - Can be forward-declared when the definition of Enumeration is unknown.
|
||||
/// - Can only be constructed from valid enumerated values.
|
||||
/// - Can only be compared with others of the same type.
|
||||
///
|
||||
/// \param Enumeration A type that contains an enum \c Value of the allowed values of the enumeration.
|
||||
template<typename Enumeration>
|
||||
class EnumeratedValue : public Enumeration
|
||||
{
|
||||
typename Enumeration::Value m_value;
|
||||
public:
|
||||
explicit EnumeratedValue(typename Enumeration::Value value) : m_value(value)
|
||||
{
|
||||
}
|
||||
typename Enumeration::Value get() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Enumeration>
|
||||
inline bool operator==(EnumeratedValue<Enumeration> self, EnumeratedValue<Enumeration> other)
|
||||
{
|
||||
return self.get() == other.get();
|
||||
}
|
||||
template<typename Enumeration>
|
||||
inline bool operator!=(EnumeratedValue<Enumeration> self, EnumeratedValue<Enumeration> other)
|
||||
{
|
||||
return !operator==(self, other);
|
||||
}
|
||||
|
||||
#endif
|
21
tools/urt/libs/generic/object.cpp
Normal file
21
tools/urt/libs/generic/object.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
|
||||
#include "object.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
class Blah
|
||||
{
|
||||
int i;
|
||||
public:
|
||||
Blah()
|
||||
{
|
||||
i = 3;
|
||||
}
|
||||
};
|
||||
|
||||
void Test()
|
||||
{
|
||||
char storage[sizeof(Blah)];
|
||||
constructor(*reinterpret_cast<Blah*>(storage));
|
||||
}
|
||||
}
|
78
tools/urt/libs/generic/object.h
Normal file
78
tools/urt/libs/generic/object.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
|
||||
#if !defined(INCLUDED_GENERIC_OBJECT_H)
|
||||
#define INCLUDED_GENERIC_OBJECT_H
|
||||
|
||||
/// \file
|
||||
/// \brief Convenience functions (syntactic sugar) to wrap explicit constructor (aka in-place 'new') and destructor calls.
|
||||
///
|
||||
/// Use makeReference() to wrap non-const-reference constructor parameters.
|
||||
|
||||
#if _MSC_VER > 1000 && defined(WIN32)
|
||||
#pragma warning(disable:4345) // behavior change: an object of POD type constructed with an initializer of the form () will be default-initialized
|
||||
#endif
|
||||
|
||||
#include <new>
|
||||
|
||||
template<typename Type>
|
||||
inline void constructor(Type& object)
|
||||
{
|
||||
new(&object) Type();
|
||||
}
|
||||
|
||||
template<typename Type, typename T1>
|
||||
inline void constructor(Type& object, const T1& t1)
|
||||
{
|
||||
new(&object) Type(t1);
|
||||
}
|
||||
|
||||
template<typename Type, typename T1, typename T2>
|
||||
inline void constructor(Type& object, const T1& t1, const T2& t2)
|
||||
{
|
||||
new(&object) Type(t1, t2);
|
||||
}
|
||||
|
||||
template<typename Type, typename T1, typename T2, typename T3>
|
||||
inline void constructor(Type& object, const T1& t1, const T2& t2, const T3& t3)
|
||||
{
|
||||
new(&object) Type(t1, t2, t3);
|
||||
}
|
||||
|
||||
template<typename Type, typename T1, typename T2, typename T3, typename T4>
|
||||
inline void constructor(Type& object, const T1& t1, const T2& t2, const T3& t3, const T4& t4)
|
||||
{
|
||||
new(&object) Type(t1, t2, t3, t4);
|
||||
}
|
||||
|
||||
template<typename Type, typename T1, typename T2, typename T3, typename T4, typename T5>
|
||||
inline void constructor(Type& object, const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5)
|
||||
{
|
||||
new(&object) Type(t1, t2, t3, t4, t5);
|
||||
}
|
||||
|
||||
template<typename Type, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
|
||||
inline void constructor(Type& object, const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5, const T6& t6)
|
||||
{
|
||||
new(&object) Type(t1, t2, t3, t4, t5, t6);
|
||||
}
|
||||
|
||||
template<typename Type, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
|
||||
inline void constructor(Type& object, const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5, const T6& t6, const T7& t7)
|
||||
{
|
||||
new(&object) Type(t1, t2, t3, t4, t5, t6, t7);
|
||||
}
|
||||
|
||||
template<typename Type, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
|
||||
inline void constructor(Type& object, const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5, const T6& t6, const T7& t7, const T8& t8)
|
||||
{
|
||||
new(&object) Type(t1, t2, t3, t4, t5, t6, t7, t8);
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
inline void destructor(Type& object)
|
||||
{
|
||||
object.~Type();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
2
tools/urt/libs/generic/reference.cpp
Normal file
2
tools/urt/libs/generic/reference.cpp
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include "reference.h"
|
111
tools/urt/libs/generic/reference.h
Normal file
111
tools/urt/libs/generic/reference.h
Normal file
|
@ -0,0 +1,111 @@
|
|||
|
||||
#if !defined(INCLUDED_GENERIC_REFERENCE_H)
|
||||
#define INCLUDED_GENERIC_REFERENCE_H
|
||||
|
||||
/// \file
|
||||
/// \brief Wrappers to allow storing objects in templated containers using 'reference' semantics.
|
||||
|
||||
/// \brief A reference to a mutable object.
|
||||
/// Has 'reference' semantics, except for \c 'operator==' and \c 'operator.'.
|
||||
/// \param Type The type of the referenced object.
|
||||
template<typename Type>
|
||||
class Reference
|
||||
{
|
||||
Type* m_contained;
|
||||
public:
|
||||
explicit Reference(Type& contained) : m_contained(&contained)
|
||||
{
|
||||
}
|
||||
Type& operator*() const
|
||||
{
|
||||
return *m_contained;
|
||||
}
|
||||
Type* operator->() const
|
||||
{
|
||||
return m_contained;
|
||||
}
|
||||
operator Type&() const
|
||||
{
|
||||
return *m_contained;
|
||||
}
|
||||
Type& get() const
|
||||
{
|
||||
return *m_contained;
|
||||
}
|
||||
Type* get_pointer() const
|
||||
{
|
||||
return m_contained;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
bool operator<(const Reference<Type>& self, const Reference<Type>& other)
|
||||
{
|
||||
return self.get() < other.get();
|
||||
}
|
||||
template<typename Type>
|
||||
bool operator==(const Reference<Type>& self, const Reference<Type>& other)
|
||||
{
|
||||
return self.get() == other.get();
|
||||
}
|
||||
|
||||
/// \brief construct a reference to a mutable object.
|
||||
template<typename Type>
|
||||
inline Reference<Type> makeReference(Type& value)
|
||||
{
|
||||
return Reference<Type>(value);
|
||||
}
|
||||
|
||||
/// \brief A reference to a non-mutable object.
|
||||
/// Has 'reference' semantics, except for \c 'operator==' and \c 'operator.'.
|
||||
/// \param Type The type of the referenced object.
|
||||
template<typename Type>
|
||||
class ConstReference
|
||||
{
|
||||
const Type* m_contained;
|
||||
public:
|
||||
explicit ConstReference(const Type& contained) : m_contained(&contained)
|
||||
{
|
||||
}
|
||||
const Type& operator*() const
|
||||
{
|
||||
return *m_contained;
|
||||
}
|
||||
const Type* operator->() const
|
||||
{
|
||||
return m_contained;
|
||||
}
|
||||
operator const Type&() const
|
||||
{
|
||||
return *m_contained;
|
||||
}
|
||||
const Type& get() const
|
||||
{
|
||||
return *m_contained;
|
||||
}
|
||||
const Type* get_pointer() const
|
||||
{
|
||||
return m_contained;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
bool operator<(const ConstReference<Type>& self, const ConstReference<Type>& other)
|
||||
{
|
||||
return self.get() < other.get();
|
||||
}
|
||||
template<typename Type>
|
||||
bool operator==(const ConstReference<Type>& self, const ConstReference<Type>& other)
|
||||
{
|
||||
return self.get() == other.get();
|
||||
}
|
||||
|
||||
/// \brief construct a reference to a non-mutable object.
|
||||
template<typename Type>
|
||||
inline ConstReference<Type> makeReference(const Type& value)
|
||||
{
|
||||
return ConstReference<Type>(value);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
2
tools/urt/libs/generic/referencecounted.cpp
Normal file
2
tools/urt/libs/generic/referencecounted.cpp
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include "referencecounted.h"
|
187
tools/urt/libs/generic/referencecounted.h
Normal file
187
tools/urt/libs/generic/referencecounted.h
Normal file
|
@ -0,0 +1,187 @@
|
|||
|
||||
#if !defined(INCLUDED_GENERIC_REFERENCECOUNTED_H)
|
||||
#define INCLUDED_GENERIC_REFERENCECOUNTED_H
|
||||
|
||||
/// \file
|
||||
/// \brief 'smart' pointers and references.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
template<typename Type>
|
||||
class IncRefDecRefCounter
|
||||
{
|
||||
public:
|
||||
void increment(Type& value)
|
||||
{
|
||||
value.IncRef();
|
||||
}
|
||||
void decrement(Type& value)
|
||||
{
|
||||
value.DecRef();
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief A smart-pointer that uses a counter stored in the object pointed-to.
|
||||
template<typename Type, typename Counter = IncRefDecRefCounter<Type> >
|
||||
class SmartPointer : public Counter
|
||||
{
|
||||
Type* m_value;
|
||||
public:
|
||||
|
||||
SmartPointer(const SmartPointer& other)
|
||||
: m_value(other.m_value)
|
||||
{
|
||||
Counter::increment(*m_value);
|
||||
}
|
||||
explicit SmartPointer(Type* value)
|
||||
: m_value(value)
|
||||
{
|
||||
Counter::increment(*m_value);
|
||||
}
|
||||
~SmartPointer()
|
||||
{
|
||||
Counter::decrement(*m_value);
|
||||
}
|
||||
SmartPointer& operator=(const SmartPointer& other)
|
||||
{
|
||||
SmartPointer temp(other);
|
||||
temp.swap(*this);
|
||||
return *this;
|
||||
}
|
||||
SmartPointer& operator=(Type* value)
|
||||
{
|
||||
SmartPointer temp(value);
|
||||
temp.swap(*this);
|
||||
return *this;
|
||||
}
|
||||
void swap(SmartPointer& other)
|
||||
{
|
||||
std::swap(m_value, other.m_value);
|
||||
}
|
||||
|
||||
operator Type*() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
Type& operator*() const
|
||||
{
|
||||
return *m_value;
|
||||
}
|
||||
Type* operator->() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
Type* get() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
inline bool operator<(const SmartPointer<Type>& self, const SmartPointer<Type>& other)
|
||||
{
|
||||
return self.get() < other.get();
|
||||
}
|
||||
template<typename Type>
|
||||
inline bool operator==(const SmartPointer<Type>& self, const SmartPointer<Type>& other)
|
||||
{
|
||||
return self.get() == other.get();
|
||||
}
|
||||
template<typename Type>
|
||||
inline bool operator!=(const SmartPointer<Type>& self, const SmartPointer<Type>& other)
|
||||
{
|
||||
return !::operator==(self, other);
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
/// \brief Swaps the values of \p self and \p other.
|
||||
/// Overloads std::swap().
|
||||
template<typename Type>
|
||||
inline void swap(SmartPointer<Type>& self, SmartPointer<Type>& other)
|
||||
{
|
||||
self.swap(other);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// \brief A smart-reference that uses a counter stored in the object pointed-to.
|
||||
template<typename Type, typename Counter = IncRefDecRefCounter<Type> >
|
||||
class SmartReference : public Counter
|
||||
{
|
||||
Type* m_value;
|
||||
public:
|
||||
|
||||
SmartReference(const SmartReference& other)
|
||||
: m_value(other.m_value)
|
||||
{
|
||||
Counter::increment(*m_value);
|
||||
}
|
||||
explicit SmartReference(Type& value)
|
||||
: m_value(&value)
|
||||
{
|
||||
Counter::increment(*m_value);
|
||||
}
|
||||
~SmartReference()
|
||||
{
|
||||
Counter::decrement(*m_value);
|
||||
}
|
||||
SmartReference& operator=(const SmartReference& other)
|
||||
{
|
||||
SmartReference temp(other);
|
||||
temp.swap(*this);
|
||||
return *this;
|
||||
}
|
||||
SmartReference& operator=(Type& value)
|
||||
{
|
||||
SmartReference temp(value);
|
||||
temp.swap(*this);
|
||||
return *this;
|
||||
}
|
||||
void swap(SmartReference& other)
|
||||
{
|
||||
std::swap(m_value, other.m_value);
|
||||
}
|
||||
|
||||
operator Type&() const
|
||||
{
|
||||
return *m_value;
|
||||
}
|
||||
Type& get() const
|
||||
{
|
||||
return *m_value;
|
||||
}
|
||||
Type* get_pointer() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
inline bool operator<(const SmartReference<Type>& self, const SmartReference<Type>& other)
|
||||
{
|
||||
return self.get() < other.get();
|
||||
}
|
||||
template<typename Type>
|
||||
inline bool operator==(const SmartReference<Type>& self, const SmartReference<Type>& other)
|
||||
{
|
||||
return self.get() == other.get();
|
||||
}
|
||||
template<typename Type>
|
||||
inline bool operator!=(const SmartReference<Type>& self, const SmartReference<Type>& other)
|
||||
{
|
||||
return !::operator==(self, other);
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
/// \brief Swaps the values of \p self and \p other.
|
||||
/// Overloads std::swap().
|
||||
template<typename Type>
|
||||
inline void swap(SmartReference<Type>& self, SmartReference<Type>& other)
|
||||
{
|
||||
self.swap(other);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
113
tools/urt/libs/generic/static.cpp
Normal file
113
tools/urt/libs/generic/static.cpp
Normal file
|
@ -0,0 +1,113 @@
|
|||
|
||||
#include "static.h"
|
||||
|
||||
#if defined(_DEBUG) || defined(DOXYGEN)
|
||||
|
||||
namespace ExampleStatic
|
||||
{
|
||||
// Static example
|
||||
// ---- myclass.h
|
||||
class MyClass
|
||||
{
|
||||
public:
|
||||
int value;
|
||||
MyClass() : value(3)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
typedef Static<MyClass> StaticMyClass;
|
||||
|
||||
// ---- main.cpp
|
||||
class DynamicInitialisation
|
||||
{
|
||||
public:
|
||||
DynamicInitialisation()
|
||||
{
|
||||
// StaticMyClass::instance() may be invalid here because construction order is undefined
|
||||
}
|
||||
};
|
||||
|
||||
DynamicInitialisation g_dynamicInitialisation;
|
||||
|
||||
void duringMain()
|
||||
{
|
||||
int bar = StaticMyClass::instance().value;
|
||||
}
|
||||
// end example
|
||||
}
|
||||
|
||||
namespace ExampleLazyStatic
|
||||
{
|
||||
// LazyStatic example
|
||||
// ---- myclass.h
|
||||
class MyClass
|
||||
{
|
||||
public:
|
||||
int value;
|
||||
MyClass() : value(3)
|
||||
{
|
||||
}
|
||||
// destructor will never be called
|
||||
};
|
||||
|
||||
typedef LazyStatic<MyClass> StaticMyClass;
|
||||
|
||||
// ---- main.cpp
|
||||
class DynamicInitialisation
|
||||
{
|
||||
public:
|
||||
DynamicInitialisation()
|
||||
{
|
||||
int bar = StaticMyClass::instance().value;
|
||||
}
|
||||
};
|
||||
|
||||
DynamicInitialisation g_dynamicInitialisation;
|
||||
|
||||
void duringMain()
|
||||
{
|
||||
int bar = StaticMyClass::instance().value;
|
||||
}
|
||||
// end example
|
||||
}
|
||||
|
||||
namespace ExampleSmartStatic
|
||||
{
|
||||
// SmartStatic example
|
||||
// ---- myclass.h
|
||||
class MyClass
|
||||
{
|
||||
public:
|
||||
int value;
|
||||
MyClass() : value(3)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
typedef CountedStatic<MyClass> StaticMyClass;
|
||||
|
||||
// ---- main.cpp
|
||||
class DynamicInitialisation
|
||||
{
|
||||
public:
|
||||
DynamicInitialisation()
|
||||
{
|
||||
// StaticMyClass::instance() is invalid before the ref is constructed
|
||||
SmartStatic<MyClass> ref;
|
||||
int bar = ref.instance().value;
|
||||
|
||||
SmartStatic<MyClass> ref2; // any number of instances are allowed.
|
||||
}
|
||||
};
|
||||
|
||||
DynamicInitialisation g_dynamicInitialisation;
|
||||
|
||||
void duringMain()
|
||||
{
|
||||
int bar = SmartStatic<MyClass>().instance().value; // an instance can be a temporary
|
||||
}
|
||||
// end example
|
||||
}
|
||||
|
||||
#endif
|
123
tools/urt/libs/generic/static.h
Normal file
123
tools/urt/libs/generic/static.h
Normal file
|
@ -0,0 +1,123 @@
|
|||
|
||||
#if !defined(INCLUDED_GENERIC_STATIC_H)
|
||||
#define INCLUDED_GENERIC_STATIC_H
|
||||
|
||||
/// \file
|
||||
/// \brief Template techniques for instantiating singletons.
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
/// \brief A singleton which is statically initialised.
|
||||
///
|
||||
/// \param Type The singleton object type.
|
||||
///
|
||||
/// \dontinclude generic/static.cpp
|
||||
/// \skipline Static example
|
||||
/// \until end example
|
||||
template<typename Type>
|
||||
class Static
|
||||
{
|
||||
static Type m_instance;
|
||||
public:
|
||||
static Type& instance()
|
||||
{
|
||||
return m_instance;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
Type Static<Type>::m_instance;
|
||||
|
||||
|
||||
/// \brief A singleton which is lazily initialised.
|
||||
/// The instance is constructed the first time it is referenced, and is never destroyed.
|
||||
///
|
||||
/// \param Type The singleton object type.
|
||||
///
|
||||
/// \dontinclude generic/static.cpp
|
||||
/// \skipline LazyStatic example
|
||||
/// \until end example
|
||||
template<typename Type>
|
||||
class LazyStatic
|
||||
{
|
||||
static Type* m_instance; // this will be initialised to 0 by the CRT, according to the c++ standard
|
||||
public:
|
||||
static Type& instance()
|
||||
{
|
||||
if(m_instance == 0)
|
||||
{
|
||||
m_instance = new Type; // allocate using 'new' to get the correct alignment
|
||||
}
|
||||
return *m_instance;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
Type* LazyStatic<Type>::m_instance;
|
||||
|
||||
|
||||
/// \brief A singleton which keeps a count of the number of times it is referenced.
|
||||
///
|
||||
/// The instance is constructed when its reference count changes from 0 to 1 and destroyed when its reference count changes from 1 to 0.
|
||||
/// Use with SmartStatic.
|
||||
///
|
||||
/// \param Type The singleton object type.
|
||||
template<typename Type>
|
||||
class CountedStatic
|
||||
{
|
||||
static std::size_t m_refcount; // this will be initialised to 0 by the CRT, according to the c++ standard
|
||||
static Type* m_instance;
|
||||
public:
|
||||
static Type& instance()
|
||||
{
|
||||
return *m_instance;
|
||||
}
|
||||
static void capture()
|
||||
{
|
||||
if(++m_refcount == 1)
|
||||
{
|
||||
m_instance = new Type; // allocate using 'new' to get the correct alignment
|
||||
}
|
||||
}
|
||||
static void release()
|
||||
{
|
||||
if(--m_refcount == 0)
|
||||
{
|
||||
delete m_instance;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
std::size_t CountedStatic<Type>::m_refcount; // this will be initialised to 0 by the CRT, according to the c++ standard
|
||||
template<typename Type>
|
||||
Type* CountedStatic<Type>::m_instance;
|
||||
|
||||
/// \brief A reference to a CountedStatic.
|
||||
/// Guarantees that CountedStatic<Type> will be constructed for the lifetime of this object.
|
||||
///
|
||||
/// \param Type The type parameter of the CountedStatic to reference.
|
||||
///
|
||||
/// \dontinclude generic/static.cpp
|
||||
/// \skipline SmartStatic example
|
||||
/// \until end example
|
||||
template<typename Type>
|
||||
class SmartStatic
|
||||
{
|
||||
public:
|
||||
SmartStatic()
|
||||
{
|
||||
CountedStatic<Type>::capture();
|
||||
}
|
||||
~SmartStatic()
|
||||
{
|
||||
CountedStatic<Type>::release();
|
||||
}
|
||||
Type& instance()
|
||||
{
|
||||
return CountedStatic<Type>::instance();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
527
tools/urt/libs/gtkutil/accelerator.cpp
Normal file
527
tools/urt/libs/gtkutil/accelerator.cpp
Normal file
|
@ -0,0 +1,527 @@
|
|||
|
||||
#include "accelerator.h"
|
||||
|
||||
#include "debugging/debugging.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <gtk/gtkwindow.h>
|
||||
#include <gtk/gtkaccelgroup.h>
|
||||
|
||||
#include "generic/callback.h"
|
||||
#include "generic/bitfield.h"
|
||||
|
||||
#include "pointer.h"
|
||||
#include "closure.h"
|
||||
|
||||
|
||||
|
||||
typedef std::map<Accelerator, Callback> AcceleratorMap;
|
||||
|
||||
void accelerator_map_insert(AcceleratorMap& acceleratorMap, Accelerator accelerator, const Callback& callback)
|
||||
{
|
||||
if(accelerator.key != 0)
|
||||
{
|
||||
ASSERT_MESSAGE(acceleratorMap.find(accelerator) == acceleratorMap.end(), "failed to add accelerator");
|
||||
acceleratorMap.insert(AcceleratorMap::value_type(accelerator, callback));
|
||||
}
|
||||
}
|
||||
|
||||
void accelerator_map_erase(AcceleratorMap& acceleratorMap, Accelerator accelerator)
|
||||
{
|
||||
if(accelerator.key != 0)
|
||||
{
|
||||
ASSERT_MESSAGE(acceleratorMap.find(accelerator) != acceleratorMap.end(), "failed to remove accelerator");
|
||||
acceleratorMap.erase(accelerator);
|
||||
}
|
||||
}
|
||||
|
||||
Accelerator accelerator_for_event_key(guint keyval, guint state)
|
||||
{
|
||||
keyval = gdk_keyval_to_upper(keyval);
|
||||
if(keyval == GDK_ISO_Left_Tab)
|
||||
keyval = GDK_Tab;
|
||||
return Accelerator(keyval, (GdkModifierType)(state & gtk_accelerator_get_default_mod_mask()));
|
||||
}
|
||||
|
||||
bool AcceleratorMap_activate(const AcceleratorMap& acceleratorMap, const Accelerator& accelerator)
|
||||
{
|
||||
AcceleratorMap::const_iterator i = acceleratorMap.find(accelerator);
|
||||
if(i != acceleratorMap.end())
|
||||
{
|
||||
(*i).second();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static gboolean accelerator_key_event(GtkWindow* window, GdkEventKey* event, AcceleratorMap* acceleratorMap)
|
||||
{
|
||||
return AcceleratorMap_activate(*acceleratorMap, accelerator_for_event_key(event->keyval, event->state));
|
||||
}
|
||||
|
||||
|
||||
AcceleratorMap g_special_accelerators;
|
||||
|
||||
|
||||
namespace MouseButton
|
||||
{
|
||||
enum
|
||||
{
|
||||
Left = 1 << 0,
|
||||
Right = 1 << 1,
|
||||
Middle = 1 << 2,
|
||||
};
|
||||
}
|
||||
|
||||
typedef unsigned int ButtonMask;
|
||||
|
||||
void print_buttons(ButtonMask mask)
|
||||
{
|
||||
globalOutputStream() << "button state: ";
|
||||
if((mask & MouseButton::Left) != 0)
|
||||
{
|
||||
globalOutputStream() << "Left ";
|
||||
}
|
||||
if((mask & MouseButton::Right) != 0)
|
||||
{
|
||||
globalOutputStream() << "Right ";
|
||||
}
|
||||
if((mask & MouseButton::Middle) != 0)
|
||||
{
|
||||
globalOutputStream() << "Middle ";
|
||||
}
|
||||
globalOutputStream() << "\n";
|
||||
}
|
||||
|
||||
ButtonMask ButtonMask_for_event_button(guint button)
|
||||
{
|
||||
switch(button)
|
||||
{
|
||||
case 1:
|
||||
return MouseButton::Left;
|
||||
case 2:
|
||||
return MouseButton::Middle;
|
||||
case 3:
|
||||
return MouseButton::Right;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool window_has_accel(GtkWindow* toplevel)
|
||||
{
|
||||
return g_slist_length(gtk_accel_groups_from_object(G_OBJECT(toplevel))) != 0;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
bool g_accel_enabled = true;
|
||||
}
|
||||
|
||||
bool global_accel_enabled()
|
||||
{
|
||||
return g_accel_enabled;
|
||||
}
|
||||
|
||||
|
||||
AcceleratorMap g_queuedAccelerators;
|
||||
|
||||
GClosure* accel_group_add_accelerator(GtkAccelGroup* group, Accelerator accelerator, const Callback& callback);
|
||||
|
||||
void GlobalQueuedAccelerators_commit()
|
||||
{
|
||||
for(AcceleratorMap::const_iterator i = g_queuedAccelerators.begin(); i != g_queuedAccelerators.end(); ++i)
|
||||
{
|
||||
accel_group_add_accelerator(global_accel, (*i).first, (*i).second);
|
||||
}
|
||||
g_queuedAccelerators.clear();
|
||||
}
|
||||
|
||||
void GlobalQueuedAccelerators_add(Accelerator accelerator, const Callback& callback)
|
||||
{
|
||||
g_queuedAccelerators.insert(AcceleratorMap::value_type(accelerator, callback));
|
||||
}
|
||||
|
||||
void accel_group_test(GtkWindow* toplevel, GtkAccelGroup* accel)
|
||||
{
|
||||
guint n_entries;
|
||||
gtk_accel_group_query(accel, '4', (GdkModifierType)0, &n_entries);
|
||||
globalOutputStream() << "grid4: " << n_entries << "\n";
|
||||
globalOutputStream() << "toplevel accelgroups: " << g_slist_length(gtk_accel_groups_from_object(G_OBJECT(toplevel))) << "\n";
|
||||
}
|
||||
|
||||
typedef std::set<GtkWindow*> WindowSet;
|
||||
WindowSet g_accel_windows;
|
||||
|
||||
bool Buttons_press(ButtonMask& buttons, guint button, guint state)
|
||||
{
|
||||
if(buttons == 0 && bitfield_enable(buttons, ButtonMask_for_event_button(button)) != 0)
|
||||
{
|
||||
ASSERT_MESSAGE(g_accel_enabled, "Buttons_press: accelerators not enabled");
|
||||
g_accel_enabled = false;
|
||||
for(WindowSet::iterator i = g_accel_windows.begin(); i != g_accel_windows.end(); ++i)
|
||||
{
|
||||
GtkWindow* toplevel = *i;
|
||||
ASSERT_MESSAGE(window_has_accel(toplevel), "ERROR");
|
||||
ASSERT_MESSAGE(GTK_WIDGET_TOPLEVEL(toplevel), "disabling accel for non-toplevel window");
|
||||
gtk_window_remove_accel_group(toplevel, global_accel);
|
||||
#if 0
|
||||
globalOutputStream() << reinterpret_cast<unsigned int>(toplevel) << ": disabled global accelerators\n";
|
||||
#endif
|
||||
#if 0
|
||||
accel_group_test(toplevel, global_accel);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
buttons = bitfield_enable(buttons, ButtonMask_for_event_button(button));
|
||||
#if 0
|
||||
globalOutputStream() << "Buttons_press: ";
|
||||
print_buttons(buttons);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Buttons_release(ButtonMask& buttons, guint button, guint state)
|
||||
{
|
||||
if(buttons != 0 && bitfield_disable(buttons, ButtonMask_for_event_button(button)) == 0)
|
||||
{
|
||||
ASSERT_MESSAGE(!g_accel_enabled, "Buttons_release: accelerators are enabled");
|
||||
g_accel_enabled = true;
|
||||
for(WindowSet::iterator i = g_accel_windows.begin(); i != g_accel_windows.end(); ++i)
|
||||
{
|
||||
GtkWindow* toplevel = *i;
|
||||
ASSERT_MESSAGE(!window_has_accel(toplevel), "ERROR");
|
||||
ASSERT_MESSAGE(GTK_WIDGET_TOPLEVEL(toplevel), "enabling accel for non-toplevel window");
|
||||
gtk_window_add_accel_group(toplevel, global_accel);
|
||||
#if 0
|
||||
globalOutputStream() << reinterpret_cast<unsigned int>(toplevel) << ": enabled global accelerators\n";
|
||||
#endif
|
||||
#if 0
|
||||
accel_group_test(toplevel, global_accel);
|
||||
#endif
|
||||
}
|
||||
GlobalQueuedAccelerators_commit();
|
||||
}
|
||||
buttons = bitfield_disable(buttons, ButtonMask_for_event_button(button));
|
||||
#if 0
|
||||
globalOutputStream() << "Buttons_release: ";
|
||||
print_buttons(buttons);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Buttons_releaseAll(ButtonMask& buttons)
|
||||
{
|
||||
Buttons_release(buttons, MouseButton::Left | MouseButton::Middle | MouseButton::Right, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct PressedButtons
|
||||
{
|
||||
ButtonMask buttons;
|
||||
|
||||
PressedButtons() : buttons(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
gboolean PressedButtons_button_press(GtkWidget* widget, GdkEventButton* event, PressedButtons* pressed)
|
||||
{
|
||||
if(event->type == GDK_BUTTON_PRESS)
|
||||
{
|
||||
return Buttons_press(pressed->buttons, event->button, event->state);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean PressedButtons_button_release(GtkWidget* widget, GdkEventButton* event, PressedButtons* pressed)
|
||||
{
|
||||
if(event->type == GDK_BUTTON_RELEASE)
|
||||
{
|
||||
return Buttons_release(pressed->buttons, event->button, event->state);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean PressedButtons_focus_out(GtkWidget* widget, GdkEventFocus* event, PressedButtons* pressed)
|
||||
{
|
||||
Buttons_releaseAll(pressed->buttons);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void PressedButtons_connect(PressedButtons& pressedButtons, GtkWidget* widget)
|
||||
{
|
||||
g_signal_connect(G_OBJECT(widget), "button_press_event", G_CALLBACK(PressedButtons_button_press), &pressedButtons);
|
||||
g_signal_connect(G_OBJECT(widget), "button_release_event", G_CALLBACK(PressedButtons_button_release), &pressedButtons);
|
||||
g_signal_connect(G_OBJECT(widget), "focus_out_event", G_CALLBACK(PressedButtons_focus_out), &pressedButtons);
|
||||
}
|
||||
|
||||
PressedButtons g_pressedButtons;
|
||||
|
||||
|
||||
#include <set>
|
||||
|
||||
struct PressedKeys
|
||||
{
|
||||
typedef std::set<guint> Keys;
|
||||
Keys keys;
|
||||
std::size_t refcount;
|
||||
|
||||
PressedKeys() : refcount(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
AcceleratorMap g_keydown_accelerators;
|
||||
AcceleratorMap g_keyup_accelerators;
|
||||
|
||||
bool Keys_press(PressedKeys::Keys& keys, guint keyval)
|
||||
{
|
||||
if(keys.insert(keyval).second)
|
||||
{
|
||||
return AcceleratorMap_activate(g_keydown_accelerators, accelerator_for_event_key(keyval, 0));
|
||||
}
|
||||
return g_keydown_accelerators.find(accelerator_for_event_key(keyval, 0)) != g_keydown_accelerators.end();
|
||||
}
|
||||
|
||||
bool Keys_release(PressedKeys::Keys& keys, guint keyval)
|
||||
{
|
||||
if(keys.erase(keyval) != 0)
|
||||
{
|
||||
return AcceleratorMap_activate(g_keyup_accelerators, accelerator_for_event_key(keyval, 0));
|
||||
}
|
||||
return g_keyup_accelerators.find(accelerator_for_event_key(keyval, 0)) != g_keyup_accelerators.end();
|
||||
}
|
||||
|
||||
void Keys_releaseAll(PressedKeys::Keys& keys, guint state)
|
||||
{
|
||||
for(PressedKeys::Keys::iterator i = keys.begin(); i != keys.end(); ++i)
|
||||
{
|
||||
AcceleratorMap_activate(g_keyup_accelerators, accelerator_for_event_key(*i, state));
|
||||
}
|
||||
keys.clear();
|
||||
}
|
||||
|
||||
gboolean PressedKeys_key_press(GtkWidget* widget, GdkEventKey* event, PressedKeys* pressedKeys)
|
||||
{
|
||||
//globalOutputStream() << "pressed: " << event->keyval << "\n";
|
||||
return event->state == 0 && Keys_press(pressedKeys->keys, event->keyval);
|
||||
}
|
||||
|
||||
gboolean PressedKeys_key_release(GtkWidget* widget, GdkEventKey* event, PressedKeys* pressedKeys)
|
||||
{
|
||||
//globalOutputStream() << "released: " << event->keyval << "\n";
|
||||
return Keys_release(pressedKeys->keys, event->keyval);
|
||||
}
|
||||
|
||||
gboolean PressedKeys_focus_in(GtkWidget* widget, GdkEventFocus* event, PressedKeys* pressedKeys)
|
||||
{
|
||||
++pressedKeys->refcount;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean PressedKeys_focus_out(GtkWidget* widget, GdkEventFocus* event, PressedKeys* pressedKeys)
|
||||
{
|
||||
if(--pressedKeys->refcount == 0)
|
||||
{
|
||||
Keys_releaseAll(pressedKeys->keys, 0);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PressedKeys g_pressedKeys;
|
||||
|
||||
void GlobalPressedKeys_releaseAll()
|
||||
{
|
||||
Keys_releaseAll(g_pressedKeys.keys, 0);
|
||||
}
|
||||
|
||||
void GlobalPressedKeys_connect(GtkWindow* window)
|
||||
{
|
||||
unsigned int key_press_handler = g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(PressedKeys_key_press), &g_pressedKeys);
|
||||
unsigned int key_release_handler = g_signal_connect(G_OBJECT(window), "key_release_event", G_CALLBACK(PressedKeys_key_release), &g_pressedKeys);
|
||||
g_object_set_data(G_OBJECT(window), "key_press_handler", gint_to_pointer(key_press_handler));
|
||||
g_object_set_data(G_OBJECT(window), "key_release_handler", gint_to_pointer(key_release_handler));
|
||||
unsigned int focus_in_handler = g_signal_connect(G_OBJECT(window), "focus_in_event", G_CALLBACK(PressedKeys_focus_in), &g_pressedKeys);
|
||||
unsigned int focus_out_handler = g_signal_connect(G_OBJECT(window), "focus_out_event", G_CALLBACK(PressedKeys_focus_out), &g_pressedKeys);
|
||||
g_object_set_data(G_OBJECT(window), "focus_in_handler", gint_to_pointer(focus_in_handler));
|
||||
g_object_set_data(G_OBJECT(window), "focus_out_handler", gint_to_pointer(focus_out_handler));
|
||||
}
|
||||
|
||||
void GlobalPressedKeys_disconnect(GtkWindow* window)
|
||||
{
|
||||
g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "key_press_handler")));
|
||||
g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "key_release_handler")));
|
||||
g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "focus_in_handler")));
|
||||
g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "focus_out_handler")));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void special_accelerators_add(Accelerator accelerator, const Callback& callback)
|
||||
{
|
||||
accelerator_map_insert(g_special_accelerators, accelerator, callback);
|
||||
}
|
||||
void special_accelerators_remove(Accelerator accelerator)
|
||||
{
|
||||
accelerator_map_erase(g_special_accelerators, accelerator);
|
||||
}
|
||||
|
||||
void keydown_accelerators_add(Accelerator accelerator, const Callback& callback)
|
||||
{
|
||||
accelerator_map_insert(g_keydown_accelerators, accelerator, callback);
|
||||
}
|
||||
void keydown_accelerators_remove(Accelerator accelerator)
|
||||
{
|
||||
accelerator_map_erase(g_keydown_accelerators, accelerator);
|
||||
}
|
||||
|
||||
void keyup_accelerators_add(Accelerator accelerator, const Callback& callback)
|
||||
{
|
||||
accelerator_map_insert(g_keyup_accelerators, accelerator, callback);
|
||||
}
|
||||
void keyup_accelerators_remove(Accelerator accelerator)
|
||||
{
|
||||
accelerator_map_erase(g_keyup_accelerators, accelerator);
|
||||
}
|
||||
|
||||
|
||||
gboolean accel_closure_callback(GtkAccelGroup* group, GtkWidget* widget, guint key, GdkModifierType modifiers, gpointer data)
|
||||
{
|
||||
(*reinterpret_cast<Callback*>(data))();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GClosure* accel_group_add_accelerator(GtkAccelGroup* group, Accelerator accelerator, const Callback& callback)
|
||||
{
|
||||
if(accelerator.key != 0 && gtk_accelerator_valid(accelerator.key, accelerator.modifiers))
|
||||
{
|
||||
//globalOutputStream() << "adding accelerator: " << accelerator.key << " " << accelerator.modifiers << "\n";
|
||||
GClosure* closure = create_cclosure(G_CALLBACK(accel_closure_callback), callback);
|
||||
gtk_accel_group_connect(group, accelerator.key, accelerator.modifiers, GTK_ACCEL_VISIBLE, closure);
|
||||
return closure;
|
||||
}
|
||||
else
|
||||
{
|
||||
special_accelerators_add(accelerator, callback);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void accel_group_remove_accelerator(GtkAccelGroup* group, Accelerator accelerator)
|
||||
{
|
||||
if(accelerator.key != 0 && gtk_accelerator_valid(accelerator.key, accelerator.modifiers))
|
||||
{
|
||||
gtk_accel_group_disconnect_key(group, accelerator.key, accelerator.modifiers);
|
||||
}
|
||||
else
|
||||
{
|
||||
special_accelerators_remove(accelerator);
|
||||
}
|
||||
}
|
||||
|
||||
GtkAccelGroup* global_accel = 0;
|
||||
|
||||
void global_accel_init()
|
||||
{
|
||||
global_accel = gtk_accel_group_new();
|
||||
}
|
||||
|
||||
void global_accel_destroy()
|
||||
{
|
||||
g_object_unref(global_accel);
|
||||
}
|
||||
|
||||
GClosure* global_accel_group_add_accelerator(Accelerator accelerator, const Callback& callback)
|
||||
{
|
||||
if(!global_accel_enabled())
|
||||
{
|
||||
// workaround: cannot add to GtkAccelGroup while it is disabled
|
||||
GlobalQueuedAccelerators_add(accelerator, callback);
|
||||
return 0;
|
||||
}
|
||||
return accel_group_add_accelerator(global_accel, accelerator, callback);
|
||||
}
|
||||
void global_accel_group_remove_accelerator(Accelerator accelerator)
|
||||
{
|
||||
//ASSERT_MESSAGE(global_accel_enabled(), "removing accelerator while global accel is disabled");
|
||||
accel_group_remove_accelerator(global_accel, accelerator);
|
||||
}
|
||||
|
||||
/// \brief Propagates key events to the focus-widget, overriding global accelerators.
|
||||
static gboolean override_global_accelerators(GtkWindow* window, GdkEventKey* event, gpointer data)
|
||||
{
|
||||
return gtk_window_propagate_key_event(window, event);
|
||||
}
|
||||
|
||||
void global_accel_connect_window(GtkWindow* window)
|
||||
{
|
||||
#if 1
|
||||
unsigned int override_handler = g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(override_global_accelerators), 0);
|
||||
g_object_set_data(G_OBJECT(window), "override_handler", gint_to_pointer(override_handler));
|
||||
|
||||
unsigned int special_key_press_handler = g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(accelerator_key_event), &g_special_accelerators);
|
||||
g_object_set_data(G_OBJECT(window), "special_key_press_handler", gint_to_pointer(special_key_press_handler));
|
||||
|
||||
GlobalPressedKeys_connect(window);
|
||||
#else
|
||||
unsigned int key_press_handler = g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(accelerator_key_event), &g_keydown_accelerators);
|
||||
unsigned int key_release_handler = g_signal_connect(G_OBJECT(window), "key_release_event", G_CALLBACK(accelerator_key_event), &g_keyup_accelerators);
|
||||
g_object_set_data(G_OBJECT(window), "key_press_handler", gint_to_pointer(key_press_handler));
|
||||
g_object_set_data(G_OBJECT(window), "key_release_handler", gint_to_pointer(key_release_handler));
|
||||
#endif
|
||||
g_accel_windows.insert(window);
|
||||
gtk_window_add_accel_group(window, global_accel);
|
||||
}
|
||||
void global_accel_disconnect_window(GtkWindow* window)
|
||||
{
|
||||
#if 1
|
||||
GlobalPressedKeys_disconnect(window);
|
||||
|
||||
g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "override_handler")));
|
||||
g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "special_key_press_handler")));
|
||||
#else
|
||||
g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "key_press_handler")));
|
||||
g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "key_release_handler")));
|
||||
#endif
|
||||
gtk_window_remove_accel_group(window, global_accel);
|
||||
std::size_t count = g_accel_windows.erase(window);
|
||||
ASSERT_MESSAGE(count == 1, "failed to remove accel group\n");
|
||||
}
|
||||
|
||||
|
||||
GClosure* global_accel_group_find(Accelerator accelerator)
|
||||
{
|
||||
guint numEntries = 0;
|
||||
GtkAccelGroupEntry* entry = gtk_accel_group_query(global_accel, accelerator.key, accelerator.modifiers, &numEntries);
|
||||
if(numEntries != 0)
|
||||
{
|
||||
if(numEntries != 1)
|
||||
{
|
||||
char* name = gtk_accelerator_name(accelerator.key, accelerator.modifiers);
|
||||
globalErrorStream() << "accelerator already in-use: " << name << "\n";
|
||||
g_free(name);
|
||||
}
|
||||
return entry->closure;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void command_connect_accelerator(const Accelerator& accelerator, const Callback& callback)
|
||||
{
|
||||
if(accelerator.key != 0)
|
||||
{
|
||||
global_accel_group_add_accelerator(accelerator, callback);
|
||||
}
|
||||
}
|
||||
|
||||
void command_disconnect_accelerator(const Accelerator& accelerator, const Callback& callback)
|
||||
{
|
||||
if(accelerator.key != 0)
|
||||
{
|
||||
global_accel_group_remove_accelerator(accelerator);
|
||||
}
|
||||
}
|
||||
|
||||
|
97
tools/urt/libs/gtkutil/accelerator.h
Normal file
97
tools/urt/libs/gtkutil/accelerator.h
Normal file
|
@ -0,0 +1,97 @@
|
|||
|
||||
#if !defined(INCLUDED_GTKUTIL_ACCELERATOR_H)
|
||||
#define INCLUDED_GTKUTIL_ACCELERATOR_H
|
||||
|
||||
#include <gdk/gdktypes.h>
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
|
||||
#include "generic/callback.h"
|
||||
|
||||
|
||||
struct Accelerator
|
||||
{
|
||||
Accelerator(guint _key)
|
||||
: key(_key), modifiers((GdkModifierType)0)
|
||||
{
|
||||
}
|
||||
Accelerator(guint _key, GdkModifierType _modifiers)
|
||||
: key(_key), modifiers(_modifiers)
|
||||
{
|
||||
}
|
||||
bool operator<(const Accelerator& other) const
|
||||
{
|
||||
return key < other.key || (!(other.key < key) && modifiers < other.modifiers);
|
||||
}
|
||||
guint key;
|
||||
GdkModifierType modifiers;
|
||||
};
|
||||
|
||||
inline Accelerator accelerator_null()
|
||||
{
|
||||
return Accelerator(0, (GdkModifierType)0);
|
||||
}
|
||||
|
||||
|
||||
void keydown_accelerators_add(Accelerator accelerator, const Callback& callback);
|
||||
void keydown_accelerators_remove(Accelerator accelerator);
|
||||
void keyup_accelerators_add(Accelerator accelerator, const Callback& callback);
|
||||
void keyup_accelerators_remove(Accelerator accelerator);
|
||||
|
||||
typedef struct _GtkWidget GtkWidget;
|
||||
typedef struct _GtkWindow GtkWindow;
|
||||
void global_accel_connect_window(GtkWindow* window);
|
||||
void global_accel_disconnect_window(GtkWindow* window);
|
||||
|
||||
void GlobalPressedKeys_releaseAll();
|
||||
|
||||
typedef struct _GtkAccelGroup GtkAccelGroup;
|
||||
extern GtkAccelGroup* global_accel;
|
||||
void global_accel_init();
|
||||
void global_accel_destroy();
|
||||
|
||||
GClosure* global_accel_group_find(Accelerator accelerator);
|
||||
|
||||
void command_connect_accelerator(const Accelerator& accelerator, const Callback& callback);
|
||||
void command_disconnect_accelerator(const Accelerator& accelerator, const Callback& callback);
|
||||
|
||||
|
||||
class Command
|
||||
{
|
||||
public:
|
||||
Callback m_callback;
|
||||
const Accelerator& m_accelerator;
|
||||
Command(const Callback& callback, const Accelerator& accelerator) : m_callback(callback), m_accelerator(accelerator)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class Toggle
|
||||
{
|
||||
public:
|
||||
Command m_command;
|
||||
BoolExportCallback m_exportCallback;
|
||||
Toggle(const Callback& callback, const Accelerator& accelerator, const BoolExportCallback& exportCallback) : m_command(callback, accelerator), m_exportCallback(exportCallback)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class KeyEvent
|
||||
{
|
||||
public:
|
||||
const Accelerator& m_accelerator;
|
||||
Callback m_keyDown;
|
||||
Callback m_keyUp;
|
||||
KeyEvent(const Accelerator& accelerator, const Callback& keyDown, const Callback& keyUp) : m_accelerator(accelerator), m_keyDown(keyDown), m_keyUp(keyUp)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct PressedButtons;
|
||||
typedef struct _GtkWidget GtkWidget;
|
||||
void PressedButtons_connect(PressedButtons& pressedButtons, GtkWidget* widget);
|
||||
|
||||
extern PressedButtons g_pressedButtons;
|
||||
|
||||
#endif
|
117
tools/urt/libs/gtkutil/button.cpp
Normal file
117
tools/urt/libs/gtkutil/button.cpp
Normal file
|
@ -0,0 +1,117 @@
|
|||
|
||||
#include "button.h"
|
||||
|
||||
#include <gtk/gtkradiobutton.h>
|
||||
|
||||
#include "stream/textstream.h"
|
||||
#include "stream/stringstream.h"
|
||||
#include "generic/callback.h"
|
||||
|
||||
#include "image.h"
|
||||
#include "pointer.h"
|
||||
|
||||
void clicked_closure_callback(GtkWidget* widget, gpointer data)
|
||||
{
|
||||
(*reinterpret_cast<Callback*>(data))();
|
||||
}
|
||||
|
||||
void button_connect_callback(GtkButton* button, const Callback& callback)
|
||||
{
|
||||
#if 1
|
||||
g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(callback.getThunk()), callback.getEnvironment());
|
||||
#else
|
||||
g_signal_connect_closure(G_OBJECT(button), "clicked", create_cclosure(G_CALLBACK(clicked_closure_callback), callback), FALSE);
|
||||
#endif
|
||||
}
|
||||
|
||||
guint toggle_button_connect_callback(GtkToggleButton* button, const Callback& callback)
|
||||
{
|
||||
#if 1
|
||||
guint handler = g_signal_connect_swapped(G_OBJECT(button), "toggled", G_CALLBACK(callback.getThunk()), callback.getEnvironment());
|
||||
#else
|
||||
guint handler = g_signal_connect_closure(G_OBJECT(button), "toggled", create_cclosure(G_CALLBACK(clicked_closure_callback), callback), TRUE);
|
||||
#endif
|
||||
g_object_set_data(G_OBJECT(button), "handler", gint_to_pointer(handler));
|
||||
return handler;
|
||||
}
|
||||
|
||||
void button_set_icon(GtkButton* button, const char* icon)
|
||||
{
|
||||
GtkImage* image = new_local_image(icon);
|
||||
gtk_widget_show(GTK_WIDGET(image));
|
||||
gtk_container_add(GTK_CONTAINER(button), GTK_WIDGET(image));
|
||||
}
|
||||
|
||||
void toggle_button_set_active_no_signal(GtkToggleButton* button, gboolean active)
|
||||
{
|
||||
//globalOutputStream() << "set active: " << active << "\n";
|
||||
guint handler_id = gpointer_to_int(g_object_get_data(G_OBJECT(button), "handler"));
|
||||
//guint signal_id = g_signal_lookup("toggled", G_OBJECT_TYPE (button));
|
||||
//globalOutputStream() << "signal_id: " << signal_id << "\n";
|
||||
//guint found = g_signal_handler_find(G_OBJECT(button), G_SIGNAL_MATCH_ID, signal_id, 0, 0, 0, 0);
|
||||
//globalOutputStream() << " handler found: " << found << "\n";
|
||||
g_signal_handler_block(G_OBJECT(button), handler_id);
|
||||
gtk_toggle_button_set_active(button, active);
|
||||
g_signal_handler_unblock(G_OBJECT(button), handler_id);
|
||||
}
|
||||
|
||||
|
||||
void radio_button_print_state(GtkRadioButton* button)
|
||||
{
|
||||
globalOutputStream() << "toggle button: ";
|
||||
for(GSList* radio = gtk_radio_button_group(button); radio != 0; radio = g_slist_next(radio))
|
||||
{
|
||||
globalOutputStream() << gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio->data));
|
||||
}
|
||||
globalOutputStream() << "\n";
|
||||
}
|
||||
|
||||
GtkToggleButton* radio_button_get_nth(GtkRadioButton* radio, int index)
|
||||
{
|
||||
GSList *group = gtk_radio_button_group(radio);
|
||||
return GTK_TOGGLE_BUTTON(g_slist_nth_data(group, g_slist_length(group) - index - 1));
|
||||
}
|
||||
|
||||
void radio_button_set_active(GtkRadioButton* radio, int index)
|
||||
{
|
||||
//radio_button_print_state(radio);
|
||||
gtk_toggle_button_set_active(radio_button_get_nth(radio, index), TRUE);
|
||||
//radio_button_print_state(radio);
|
||||
}
|
||||
|
||||
void radio_button_set_active_no_signal(GtkRadioButton* radio, int index)
|
||||
{
|
||||
{
|
||||
for(GSList* l = gtk_radio_button_get_group(radio); l != 0; l = g_slist_next(l))
|
||||
{
|
||||
g_signal_handler_block(G_OBJECT(l->data), gpointer_to_int(g_object_get_data(G_OBJECT(l->data), "handler")));
|
||||
}
|
||||
}
|
||||
radio_button_set_active(radio, index);
|
||||
{
|
||||
for(GSList* l = gtk_radio_button_get_group(radio); l != 0; l = g_slist_next(l))
|
||||
{
|
||||
g_signal_handler_unblock(G_OBJECT(l->data), gpointer_to_int(g_object_get_data(G_OBJECT(l->data), "handler")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int radio_button_get_active(GtkRadioButton* radio)
|
||||
{
|
||||
//radio_button_print_state(radio);
|
||||
GSList *group = gtk_radio_button_group(radio);
|
||||
int index = g_slist_length(group) - 1;
|
||||
for (; group != 0; group = g_slist_next(group))
|
||||
{
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(group->data)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
index--;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
23
tools/urt/libs/gtkutil/button.h
Normal file
23
tools/urt/libs/gtkutil/button.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
|
||||
#if !defined(INCLUDED_GTKUTIL_BUTTON_H)
|
||||
#define INCLUDED_GTKUTIL_BUTTON_H
|
||||
|
||||
typedef struct _GtkButton GtkButton;
|
||||
typedef struct _GtkToggleButton GtkToggleButton;
|
||||
typedef struct _GtkRadioButton GtkRadioButton;
|
||||
typedef int gint;
|
||||
typedef gint gboolean;
|
||||
typedef unsigned int guint;
|
||||
class Callback;
|
||||
|
||||
void button_connect_callback(GtkButton* button, const Callback& callback);
|
||||
guint toggle_button_connect_callback(GtkToggleButton* button, const Callback& callback);
|
||||
|
||||
void button_set_icon(GtkButton* button, const char* icon);
|
||||
void toggle_button_set_active_no_signal(GtkToggleButton* item, gboolean active);
|
||||
|
||||
void radio_button_set_active(GtkRadioButton* radio, int index);
|
||||
void radio_button_set_active_no_signal(GtkRadioButton* radio, int index);
|
||||
int radio_button_get_active(GtkRadioButton* radio);
|
||||
|
||||
#endif
|
142
tools/urt/libs/gtkutil/clipboard.cpp
Normal file
142
tools/urt/libs/gtkutil/clipboard.cpp
Normal file
|
@ -0,0 +1,142 @@
|
|||
|
||||
#include "clipboard.h"
|
||||
|
||||
#include "stream/memstream.h"
|
||||
#include "stream/textstream.h"
|
||||
|
||||
|
||||
/// \file
|
||||
/// \brief Platform-independent GTK clipboard support.
|
||||
/// \todo Using GDK_SELECTION_CLIPBOARD fails on win32, so we use the win32 API directly for now.
|
||||
#if defined (__linux__) || defined (__APPLE__)
|
||||
|
||||
#include <gtk/gtkclipboard.h>
|
||||
|
||||
enum
|
||||
{
|
||||
RADIANT_CLIPPINGS = 23,
|
||||
};
|
||||
|
||||
static const GtkTargetEntry clipboard_targets[] = {
|
||||
{ "RADIANT_CLIPPINGS", 0, RADIANT_CLIPPINGS, },
|
||||
};
|
||||
|
||||
static void clipboard_get (GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info, gpointer data)
|
||||
{
|
||||
std::size_t len = *reinterpret_cast<std::size_t*>(data);
|
||||
const char* buffer = (len != 0) ? reinterpret_cast<const char*>(data) + sizeof(std::size_t) : 0;
|
||||
|
||||
GdkAtom type = GDK_NONE;
|
||||
if(info == clipboard_targets[0].info)
|
||||
{
|
||||
type = gdk_atom_intern(clipboard_targets[0].target, FALSE);
|
||||
}
|
||||
|
||||
gtk_selection_data_set (selection_data, type, 8, reinterpret_cast<const guchar*>(buffer), static_cast<gint>(len));
|
||||
}
|
||||
|
||||
static void clipboard_clear (GtkClipboard *clipboard, gpointer data)
|
||||
{
|
||||
delete [] reinterpret_cast<const char*>(data);
|
||||
}
|
||||
|
||||
static void clipboard_received (GtkClipboard *clipboard, GtkSelectionData *data, gpointer user_data)
|
||||
{
|
||||
if (data->length < 0)
|
||||
{
|
||||
globalErrorStream() << "Error retrieving selection\n";
|
||||
}
|
||||
else if(strcmp(gdk_atom_name(data->type), clipboard_targets[0].target) == 0)
|
||||
{
|
||||
BufferInputStream istream(reinterpret_cast<const char*>(data->data), data->length);
|
||||
(*reinterpret_cast<ClipboardPasteFunc*>(user_data))(istream);
|
||||
}
|
||||
}
|
||||
|
||||
void clipboard_copy(ClipboardCopyFunc copy)
|
||||
{
|
||||
GtkClipboard* clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
|
||||
|
||||
BufferOutputStream ostream;
|
||||
copy(ostream);
|
||||
std::size_t length = ostream.size();
|
||||
char* data = new char[length + sizeof(std::size_t)];
|
||||
*reinterpret_cast<std::size_t*>(data) = length;
|
||||
memcpy(data + sizeof(std::size_t), ostream.data(), length);
|
||||
|
||||
gtk_clipboard_set_with_data (clipboard, clipboard_targets, 1, clipboard_get, clipboard_clear, data);
|
||||
}
|
||||
|
||||
ClipboardPasteFunc g_clipboardPasteFunc = 0;
|
||||
void clipboard_paste(ClipboardPasteFunc paste)
|
||||
{
|
||||
GtkClipboard* clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
|
||||
|
||||
g_clipboardPasteFunc = paste;
|
||||
gtk_clipboard_request_contents (clipboard, gdk_atom_intern(clipboard_targets[0].target, FALSE), clipboard_received, &g_clipboardPasteFunc);
|
||||
}
|
||||
|
||||
#elif defined(WIN32)
|
||||
|
||||
const char* c_clipboard_format = "RadiantClippings";
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
void clipboard_copy(ClipboardCopyFunc copy)
|
||||
{
|
||||
BufferOutputStream ostream;
|
||||
copy(ostream);
|
||||
|
||||
bool bClipped = false;
|
||||
UINT nClipboard = ::RegisterClipboardFormat(c_clipboard_format);
|
||||
if (nClipboard > 0)
|
||||
{
|
||||
if (::OpenClipboard(0))
|
||||
{
|
||||
EmptyClipboard();
|
||||
std::size_t length = ostream.size();
|
||||
HANDLE h = ::GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, length + sizeof(std::size_t));
|
||||
if (h != 0)
|
||||
{
|
||||
char *buffer = reinterpret_cast<char*>(::GlobalLock(h));
|
||||
*reinterpret_cast<std::size_t*>(buffer) = length;
|
||||
buffer += sizeof(std::size_t);
|
||||
memcpy(buffer, ostream.data(), length);
|
||||
::GlobalUnlock(h);
|
||||
::SetClipboardData(nClipboard, h);
|
||||
::CloseClipboard();
|
||||
bClipped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!bClipped)
|
||||
{
|
||||
globalOutputStream() << "Unable to register Windows clipboard formats, copy/paste between editors will not be possible\n";
|
||||
}
|
||||
}
|
||||
|
||||
void clipboard_paste(ClipboardPasteFunc paste)
|
||||
{
|
||||
UINT nClipboard = ::RegisterClipboardFormat(c_clipboard_format);
|
||||
if (nClipboard > 0 && ::OpenClipboard(0))
|
||||
{
|
||||
if(IsClipboardFormatAvailable(nClipboard))
|
||||
{
|
||||
HANDLE h = ::GetClipboardData(nClipboard);
|
||||
if(h)
|
||||
{
|
||||
const char *buffer = reinterpret_cast<const char*>(::GlobalLock(h));
|
||||
std::size_t length = *reinterpret_cast<const std::size_t*>(buffer);
|
||||
buffer += sizeof(std::size_t);
|
||||
BufferInputStream istream(buffer, length);
|
||||
paste(istream);
|
||||
::GlobalUnlock(h);
|
||||
}
|
||||
}
|
||||
::CloseClipboard();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
13
tools/urt/libs/gtkutil/clipboard.h
Normal file
13
tools/urt/libs/gtkutil/clipboard.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
|
||||
#if !defined(INCLUDED_GTKUTIL_CLIPBOARD_H)
|
||||
#define INCLUDED_GTKUTIL_CLIPBOARD_H
|
||||
|
||||
class TextOutputStream;
|
||||
typedef void(*ClipboardCopyFunc)(TextOutputStream&);
|
||||
void clipboard_copy(ClipboardCopyFunc copy);
|
||||
|
||||
class TextInputStream;
|
||||
typedef void(*ClipboardPasteFunc)(TextInputStream&);
|
||||
void clipboard_paste(ClipboardPasteFunc paste);
|
||||
|
||||
#endif
|
3
tools/urt/libs/gtkutil/closure.cpp
Normal file
3
tools/urt/libs/gtkutil/closure.cpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
#include "closure.h"
|
||||
|
57
tools/urt/libs/gtkutil/closure.h
Normal file
57
tools/urt/libs/gtkutil/closure.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
|
||||
#if !defined(INCLUDED_GTKUTIL_CLOSURE_H)
|
||||
#define INCLUDED_GTKUTIL_CLOSURE_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include "generic/callback.h"
|
||||
|
||||
inline void closure_destroy(gpointer data, GClosure* closure)
|
||||
{
|
||||
delete reinterpret_cast<Callback*>(data);
|
||||
}
|
||||
|
||||
inline GClosure* create_cclosure(GCallback func, const Callback& callback)
|
||||
{
|
||||
return g_cclosure_new(func, new Callback(callback), closure_destroy);
|
||||
}
|
||||
|
||||
inline GValue GValue_default()
|
||||
{
|
||||
GValue value;
|
||||
value.g_type = 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
inline gint object_get_int_property(GObject* object, const char* property)
|
||||
{
|
||||
GValue gvalue = GValue_default();
|
||||
g_value_init(&gvalue, G_TYPE_INT);
|
||||
g_object_get_property(object, property, &gvalue);
|
||||
return g_value_get_int(&gvalue);
|
||||
}
|
||||
|
||||
inline void object_set_int_property(GObject* object, const char* property, gint value)
|
||||
{
|
||||
GValue gvalue = GValue_default();
|
||||
g_value_init(&gvalue, G_TYPE_INT);
|
||||
g_value_set_int(&gvalue, value);
|
||||
g_object_set_property(object, property, &gvalue);
|
||||
}
|
||||
|
||||
inline gboolean object_get_boolean_property(GObject* object, const char* property)
|
||||
{
|
||||
GValue gvalue = GValue_default();
|
||||
g_value_init(&gvalue, G_TYPE_BOOLEAN);
|
||||
g_object_get_property(object, property, &gvalue);
|
||||
return g_value_get_boolean(&gvalue);
|
||||
}
|
||||
|
||||
inline void object_set_boolean_property(GObject* object, const char* property, gboolean value)
|
||||
{
|
||||
GValue gvalue = GValue_default();
|
||||
g_value_init(&gvalue, G_TYPE_BOOLEAN);
|
||||
g_value_set_boolean(&gvalue, value);
|
||||
g_object_set_property(object, property, &gvalue);
|
||||
}
|
||||
|
||||
#endif
|
3
tools/urt/libs/gtkutil/container.cpp
Normal file
3
tools/urt/libs/gtkutil/container.cpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
#include "container.h"
|
||||
|
23
tools/urt/libs/gtkutil/container.h
Normal file
23
tools/urt/libs/gtkutil/container.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
|
||||
#if !defined(INCLUDED_GTKUTIL_CONTAINER_H)
|
||||
#define INCLUDED_GTKUTIL_CONTAINER_H
|
||||
|
||||
#include <gtk/gtkcontainer.h>
|
||||
|
||||
inline GtkWidget* container_add_widget(GtkContainer* container, GtkWidget* widget)
|
||||
{
|
||||
gtk_container_add(container, widget);
|
||||
return widget;
|
||||
}
|
||||
|
||||
inline void container_remove(GtkWidget* item, gpointer data)
|
||||
{
|
||||
gtk_container_remove(GTK_CONTAINER(data), item);
|
||||
}
|
||||
|
||||
inline void container_remove_all(GtkContainer* container)
|
||||
{
|
||||
gtk_container_foreach(container, container_remove, container);
|
||||
}
|
||||
|
||||
#endif
|
71
tools/urt/libs/gtkutil/cursor.cpp
Normal file
71
tools/urt/libs/gtkutil/cursor.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
|
||||
#include "cursor.h"
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <gdk/gdkcursor.h>
|
||||
#include <gdk/gdkpixmap.h>
|
||||
|
||||
|
||||
GdkCursor* create_blank_cursor()
|
||||
{
|
||||
GdkPixmap *pixmap;
|
||||
GdkBitmap *mask;
|
||||
char buffer [(32 * 32)/8];
|
||||
memset (buffer, 0, (32 * 32)/8);
|
||||
GdkColor white = {0, 0xffff, 0xffff, 0xffff};
|
||||
GdkColor black = {0, 0x0000, 0x0000, 0x0000};
|
||||
pixmap = gdk_bitmap_create_from_data(0, buffer, 32, 32);
|
||||
mask = gdk_bitmap_create_from_data(0, buffer, 32, 32);
|
||||
GdkCursor *cursor = gdk_cursor_new_from_pixmap(pixmap, mask, &white, &black, 1, 1);
|
||||
gdk_drawable_unref(pixmap);
|
||||
gdk_drawable_unref(mask);
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
void blank_cursor(GtkWidget* widget)
|
||||
{
|
||||
GdkCursor* cursor = create_blank_cursor();
|
||||
gdk_window_set_cursor (widget->window, cursor);
|
||||
gdk_cursor_unref(cursor);
|
||||
}
|
||||
|
||||
void default_cursor(GtkWidget* widget)
|
||||
{
|
||||
gdk_window_set_cursor(widget->window, 0);
|
||||
}
|
||||
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
#include <gdk/gdkwin32.h>
|
||||
|
||||
void Sys_GetCursorPos (int *x, int *y)
|
||||
{
|
||||
POINT pos;
|
||||
GetCursorPos(&pos);
|
||||
*x = pos.x;
|
||||
*y = pos.y;
|
||||
}
|
||||
|
||||
void Sys_SetCursorPos (int x, int y)
|
||||
{
|
||||
SetCursorPos (x, y);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <gdk/gdkx.h>
|
||||
|
||||
void Sys_GetCursorPos (int *x, int *y)
|
||||
{
|
||||
gdk_display_get_pointer(gdk_display_get_default(), 0, x, y, 0);
|
||||
}
|
||||
|
||||
void Sys_SetCursorPos (int x, int y)
|
||||
{
|
||||
XWarpPointer (GDK_DISPLAY(), None, GDK_ROOT_WINDOW(), 0, 0, 0, 0, x, y);
|
||||
}
|
||||
|
||||
#endif
|
173
tools/urt/libs/gtkutil/cursor.h
Normal file
173
tools/urt/libs/gtkutil/cursor.h
Normal file
|
@ -0,0 +1,173 @@
|
|||
|
||||
#if !defined(INCLUDED_GTKUTIL_CURSOR_H)
|
||||
#define INCLUDED_GTKUTIL_CURSOR_H
|
||||
|
||||
#include <glib/gmain.h>
|
||||
#include <gdk/gdkevents.h>
|
||||
#include <gtk/gtkwidget.h>
|
||||
|
||||
#include "debugging/debugging.h"
|
||||
|
||||
typedef struct _GdkCursor GdkCursor;
|
||||
typedef struct _GtkWidget GtkWidget;
|
||||
|
||||
GdkCursor* create_blank_cursor();
|
||||
void blank_cursor(GtkWidget* widget);
|
||||
void default_cursor(GtkWidget* widget);
|
||||
void Sys_GetCursorPos (int *x, int *y);
|
||||
void Sys_SetCursorPos (int x, int y);
|
||||
|
||||
|
||||
|
||||
class DeferredMotion
|
||||
{
|
||||
guint m_handler;
|
||||
typedef void(*MotionFunction)(gdouble x, gdouble y, guint state, void* data);
|
||||
MotionFunction m_function;
|
||||
void* m_data;
|
||||
gdouble m_x;
|
||||
gdouble m_y;
|
||||
guint m_state;
|
||||
|
||||
static gboolean deferred(DeferredMotion* self)
|
||||
{
|
||||
self->m_handler = 0;
|
||||
self->m_function(self->m_x, self->m_y, self->m_state, self->m_data);
|
||||
return FALSE;
|
||||
}
|
||||
public:
|
||||
DeferredMotion(MotionFunction function, void* data) : m_handler(0), m_function(function), m_data(data)
|
||||
{
|
||||
}
|
||||
void motion(gdouble x, gdouble y, guint state)
|
||||
{
|
||||
m_x = x;
|
||||
m_y = y;
|
||||
m_state = state;
|
||||
if(m_handler == 0)
|
||||
{
|
||||
m_handler = g_idle_add((GSourceFunc)deferred, this);
|
||||
}
|
||||
}
|
||||
static gboolean gtk_motion(GtkWidget *widget, GdkEventMotion *event, DeferredMotion* self)
|
||||
{
|
||||
self->motion(event->x, event->y, event->state);
|
||||
return FALSE;
|
||||
}
|
||||
};
|
||||
|
||||
class DeferredMotionDelta
|
||||
{
|
||||
int m_delta_x;
|
||||
int m_delta_y;
|
||||
guint m_motion_handler;
|
||||
typedef void (*MotionDeltaFunction)(int x, int y, void* data);
|
||||
MotionDeltaFunction m_function;
|
||||
void* m_data;
|
||||
|
||||
static gboolean deferred_motion(gpointer data)
|
||||
{
|
||||
reinterpret_cast<DeferredMotionDelta*>(data)->m_function(
|
||||
reinterpret_cast<DeferredMotionDelta*>(data)->m_delta_x,
|
||||
reinterpret_cast<DeferredMotionDelta*>(data)->m_delta_y,
|
||||
reinterpret_cast<DeferredMotionDelta*>(data)->m_data
|
||||
);
|
||||
reinterpret_cast<DeferredMotionDelta*>(data)->m_motion_handler = 0;
|
||||
reinterpret_cast<DeferredMotionDelta*>(data)->m_delta_x = 0;
|
||||
reinterpret_cast<DeferredMotionDelta*>(data)->m_delta_y = 0;
|
||||
return FALSE;
|
||||
}
|
||||
public:
|
||||
DeferredMotionDelta(MotionDeltaFunction function, void* data) : m_delta_x(0), m_delta_y(0), m_motion_handler(0), m_function(function), m_data(data)
|
||||
{
|
||||
}
|
||||
void flush()
|
||||
{
|
||||
if(m_motion_handler != 0)
|
||||
{
|
||||
g_source_remove(m_motion_handler);
|
||||
deferred_motion(this);
|
||||
}
|
||||
}
|
||||
void motion_delta(int x, int y, unsigned int state)
|
||||
{
|
||||
m_delta_x += x;
|
||||
m_delta_y += y;
|
||||
if(m_motion_handler == 0)
|
||||
{
|
||||
m_motion_handler = g_idle_add(deferred_motion, this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class FreezePointer
|
||||
{
|
||||
unsigned int handle_motion;
|
||||
int recorded_x, recorded_y;
|
||||
typedef void (*MotionDeltaFunction)(int x, int y, unsigned int state, void* data);
|
||||
MotionDeltaFunction m_function;
|
||||
void* m_data;
|
||||
public:
|
||||
FreezePointer() : handle_motion(0), m_function(0), m_data(0)
|
||||
{
|
||||
}
|
||||
static gboolean motion_delta(GtkWidget *widget, GdkEventMotion *event, FreezePointer* self)
|
||||
{
|
||||
int current_x, current_y;
|
||||
Sys_GetCursorPos(¤t_x, ¤t_y);
|
||||
int dx = current_x - self->recorded_x;
|
||||
int dy = current_y - self->recorded_y;
|
||||
if(dx != 0 || dy != 0)
|
||||
{
|
||||
//globalOutputStream() << "motion x: " << dx << ", y: " << dy << "\n";
|
||||
Sys_SetCursorPos(self->recorded_x, self->recorded_y);
|
||||
self->m_function(dx, dy, event->state, self->m_data);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void freeze_pointer(GtkWidget* window, MotionDeltaFunction function, void* data)
|
||||
{
|
||||
ASSERT_MESSAGE(m_function == 0, "can't freeze pointer");
|
||||
|
||||
blank_cursor(window);
|
||||
|
||||
const GdkEventMask mask = static_cast<GdkEventMask>(GDK_POINTER_MOTION_MASK
|
||||
| GDK_POINTER_MOTION_HINT_MASK
|
||||
| GDK_BUTTON_MOTION_MASK
|
||||
| GDK_BUTTON1_MOTION_MASK
|
||||
| GDK_BUTTON2_MOTION_MASK
|
||||
| GDK_BUTTON3_MOTION_MASK
|
||||
| GDK_BUTTON_PRESS_MASK
|
||||
| GDK_BUTTON_RELEASE_MASK
|
||||
| GDK_VISIBILITY_NOTIFY_MASK);
|
||||
|
||||
//GdkGrabStatus status =
|
||||
gdk_pointer_grab(window->window, TRUE, mask, window->window, 0, GDK_CURRENT_TIME);
|
||||
|
||||
Sys_GetCursorPos(&recorded_x, &recorded_y);
|
||||
|
||||
Sys_SetCursorPos(recorded_x, recorded_y);
|
||||
|
||||
m_function = function;
|
||||
m_data = data;
|
||||
|
||||
handle_motion = g_signal_connect(G_OBJECT(window), "motion_notify_event", G_CALLBACK(motion_delta), this);
|
||||
}
|
||||
|
||||
void unfreeze_pointer(GtkWidget* window)
|
||||
{
|
||||
g_signal_handler_disconnect(G_OBJECT(window), handle_motion);
|
||||
|
||||
m_function = 0;
|
||||
m_data = 0;
|
||||
|
||||
Sys_SetCursorPos(recorded_x, recorded_y);
|
||||
|
||||
gdk_pointer_ungrab(GDK_CURRENT_TIME);
|
||||
|
||||
default_cursor(window);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
283
tools/urt/libs/gtkutil/dialog.cpp
Normal file
283
tools/urt/libs/gtkutil/dialog.cpp
Normal file
|
@ -0,0 +1,283 @@
|
|||
|
||||
#include "dialog.h"
|
||||
|
||||
#include <gtk/gtkmain.h>
|
||||
#include <gtk/gtkalignment.h>
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtkvbox.h>
|
||||
#include <gtk/gtkradiobutton.h>
|
||||
#include <gtk/gtkframe.h>
|
||||
#include <gtk/gtktable.h>
|
||||
#include <gtk/gtkentry.h>
|
||||
#include <gtk/gtkbutton.h>
|
||||
#include <gtk/gtklabel.h>
|
||||
|
||||
#include "button.h"
|
||||
#include "window.h"
|
||||
|
||||
GtkVBox* create_dialog_vbox(int spacing, int border)
|
||||
{
|
||||
GtkVBox* vbox = GTK_VBOX(gtk_vbox_new(FALSE, spacing));
|
||||
gtk_widget_show(GTK_WIDGET(vbox));
|
||||
gtk_container_set_border_width(GTK_CONTAINER(vbox), border);
|
||||
return vbox;
|
||||
}
|
||||
|
||||
GtkHBox* create_dialog_hbox(int spacing, int border)
|
||||
{
|
||||
GtkHBox* hbox = GTK_HBOX(gtk_hbox_new(FALSE, spacing));
|
||||
gtk_widget_show(GTK_WIDGET(hbox));
|
||||
gtk_container_set_border_width(GTK_CONTAINER(hbox), border);
|
||||
return hbox;
|
||||
}
|
||||
|
||||
GtkFrame* create_dialog_frame(const char* label, GtkShadowType shadow)
|
||||
{
|
||||
GtkFrame* frame = GTK_FRAME(gtk_frame_new(label));
|
||||
gtk_widget_show(GTK_WIDGET(frame));
|
||||
gtk_frame_set_shadow_type(frame, shadow);
|
||||
return frame;
|
||||
}
|
||||
|
||||
GtkTable* create_dialog_table(unsigned int rows, unsigned int columns, unsigned int row_spacing, unsigned int col_spacing, int border)
|
||||
{
|
||||
GtkTable* table = GTK_TABLE(gtk_table_new(rows, columns, FALSE));
|
||||
gtk_widget_show(GTK_WIDGET(table));
|
||||
gtk_table_set_row_spacings(table, row_spacing);
|
||||
gtk_table_set_col_spacings(table, col_spacing);
|
||||
gtk_container_set_border_width(GTK_CONTAINER(table), border);
|
||||
return table;
|
||||
}
|
||||
|
||||
GtkButton* create_dialog_button(const char* label, GCallback func, gpointer data)
|
||||
{
|
||||
GtkButton* button = GTK_BUTTON(gtk_button_new_with_label(label));
|
||||
gtk_widget_set_size_request(GTK_WIDGET(button), 64, -1);
|
||||
gtk_widget_show(GTK_WIDGET(button));
|
||||
g_signal_connect(G_OBJECT(button), "clicked", func, data);
|
||||
return button;
|
||||
}
|
||||
|
||||
GtkWindow* create_dialog_window(GtkWindow* parent, const char* title, GCallback func, gpointer data, int default_w, int default_h)
|
||||
{
|
||||
GtkWindow* window = create_floating_window(title, parent);
|
||||
gtk_window_set_default_size(window, default_w, default_h);
|
||||
gtk_window_set_position(window, GTK_WIN_POS_CENTER_ON_PARENT);
|
||||
g_signal_connect(G_OBJECT(window), "delete_event", func, data);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
gboolean modal_dialog_button_clicked(GtkWidget *widget, ModalDialogButton* button)
|
||||
{
|
||||
button->m_dialog.loop = false;
|
||||
button->m_dialog.ret = button->m_value;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean modal_dialog_delete(GtkWidget *widget, GdkEvent* event, ModalDialog* dialog)
|
||||
{
|
||||
dialog->loop = 0;
|
||||
dialog->ret = eIDCANCEL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
EMessageBoxReturn modal_dialog_show(GtkWindow* window, ModalDialog& dialog)
|
||||
{
|
||||
gtk_grab_add(GTK_WIDGET(window));
|
||||
gtk_widget_show(GTK_WIDGET(window));
|
||||
|
||||
dialog.loop = true;
|
||||
while(dialog.loop)
|
||||
{
|
||||
gtk_main_iteration();
|
||||
}
|
||||
|
||||
gtk_widget_hide(GTK_WIDGET(window));
|
||||
gtk_grab_remove(GTK_WIDGET(window));
|
||||
|
||||
return dialog.ret;
|
||||
}
|
||||
|
||||
GtkButton* create_modal_dialog_button(const char* label, ModalDialogButton& button)
|
||||
{
|
||||
return create_dialog_button(label, G_CALLBACK(modal_dialog_button_clicked), &button);
|
||||
}
|
||||
|
||||
GtkWindow* create_modal_dialog_window(GtkWindow* parent, const char* title, ModalDialog& dialog, int default_w, int default_h)
|
||||
{
|
||||
return create_dialog_window(parent, title, G_CALLBACK(modal_dialog_delete), &dialog, default_w, default_h);
|
||||
}
|
||||
|
||||
GtkWindow* create_fixedsize_modal_dialog_window(GtkWindow* parent, const char* title, ModalDialog& dialog, int width, int height)
|
||||
{
|
||||
GtkWindow* window = create_modal_dialog_window(parent, title, dialog, width, height);
|
||||
|
||||
gtk_window_set_resizable(window, FALSE);
|
||||
gtk_window_set_modal(window, TRUE);
|
||||
gtk_window_set_position(window, GTK_WIN_POS_CENTER);
|
||||
|
||||
window_remove_minmax(window);
|
||||
|
||||
//gtk_widget_set_size_request(GTK_WIDGET(window), width, height);
|
||||
//gtk_window_set_default_size(window, width, height);
|
||||
//gtk_window_resize(window, width, height);
|
||||
//GdkGeometry geometry = { width, height, -1, -1, width, height, -1, -1, -1, -1, GDK_GRAVITY_STATIC, };
|
||||
//gtk_window_set_geometry_hints(window, GTK_WIDGET(window), &geometry, (GdkWindowHints)(GDK_HINT_POS|GDK_HINT_MIN_SIZE|GDK_HINT_BASE_SIZE));
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
gboolean dialog_button_ok(GtkWidget *widget, ModalDialog* data)
|
||||
{
|
||||
data->loop = false;
|
||||
data->ret = eIDOK;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean dialog_button_cancel(GtkWidget *widget, ModalDialog* data)
|
||||
{
|
||||
data->loop = false;
|
||||
data->ret = eIDCANCEL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean dialog_button_yes(GtkWidget *widget, ModalDialog* data)
|
||||
{
|
||||
data->loop = false;
|
||||
data->ret = eIDYES;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean dialog_button_no(GtkWidget *widget, ModalDialog* data)
|
||||
{
|
||||
data->loop = false;
|
||||
data->ret = eIDNO;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean dialog_delete_callback(GtkWidget *widget, GdkEventAny* event, ModalDialog* data)
|
||||
{
|
||||
gtk_widget_hide(widget);
|
||||
data->loop = false;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GtkWindow* create_simple_modal_dialog_window(const char* title, ModalDialog& dialog, GtkWidget* contents)
|
||||
{
|
||||
GtkWindow* window = create_fixedsize_modal_dialog_window(0, title, dialog);
|
||||
|
||||
GtkVBox* vbox1 = create_dialog_vbox(8, 4);
|
||||
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox1));
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(vbox1), contents);
|
||||
|
||||
GtkAlignment* alignment = GTK_ALIGNMENT(gtk_alignment_new(0.5, 0.0, 0.0, 0.0));
|
||||
gtk_widget_show(GTK_WIDGET(alignment));
|
||||
gtk_box_pack_start(GTK_BOX(vbox1), GTK_WIDGET(alignment), FALSE, FALSE, 0);
|
||||
|
||||
GtkButton* button = create_dialog_button("OK", G_CALLBACK(dialog_button_ok), &dialog);
|
||||
gtk_container_add(GTK_CONTAINER(alignment), GTK_WIDGET(button));
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
RadioHBox RadioHBox_new(StringArrayRange names)
|
||||
{
|
||||
GtkHBox* hbox = GTK_HBOX(gtk_hbox_new(TRUE, 4));
|
||||
gtk_widget_show(GTK_WIDGET(hbox));
|
||||
|
||||
GSList* group = 0;
|
||||
GtkRadioButton* radio = 0;
|
||||
for(StringArrayRange::Iterator i = names.begin; i != names.end; ++i)
|
||||
{
|
||||
radio = GTK_RADIO_BUTTON(gtk_radio_button_new_with_label(group, *i));
|
||||
gtk_widget_show(GTK_WIDGET(radio));
|
||||
gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(radio), FALSE, FALSE, 0);
|
||||
|
||||
group = gtk_radio_button_get_group(radio);
|
||||
}
|
||||
|
||||
return RadioHBox(hbox, radio);
|
||||
}
|
||||
|
||||
|
||||
PathEntry PathEntry_new()
|
||||
{
|
||||
GtkFrame* frame = GTK_FRAME(gtk_frame_new(NULL));
|
||||
gtk_widget_show(GTK_WIDGET(frame));
|
||||
gtk_frame_set_shadow_type(frame, GTK_SHADOW_IN);
|
||||
|
||||
// path entry
|
||||
GtkHBox* hbox = GTK_HBOX(gtk_hbox_new(FALSE, 0));
|
||||
gtk_widget_show(GTK_WIDGET(hbox));
|
||||
|
||||
GtkEntry* entry = GTK_ENTRY(gtk_entry_new());
|
||||
gtk_entry_set_has_frame(entry, FALSE);
|
||||
gtk_widget_show(GTK_WIDGET(entry));
|
||||
gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(entry), TRUE, TRUE, 0);
|
||||
|
||||
// browse button
|
||||
GtkButton* button = GTK_BUTTON(gtk_button_new());
|
||||
button_set_icon(button, "ellipsis.bmp");
|
||||
gtk_widget_show(GTK_WIDGET(button));
|
||||
gtk_box_pack_end(GTK_BOX(hbox), GTK_WIDGET(button), FALSE, FALSE, 0);
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(hbox));
|
||||
|
||||
return PathEntry(frame, entry, button);
|
||||
}
|
||||
|
||||
void PathEntry_setPath(PathEntry& self, const char* path)
|
||||
{
|
||||
gtk_entry_set_text(self.m_entry, path);
|
||||
}
|
||||
typedef ReferenceCaller1<PathEntry, const char*, PathEntry_setPath> PathEntrySetPathCaller;
|
||||
|
||||
void BrowsedPathEntry_clicked(GtkWidget* widget, BrowsedPathEntry* self)
|
||||
{
|
||||
self->m_browse(PathEntrySetPathCaller(self->m_entry));
|
||||
}
|
||||
|
||||
BrowsedPathEntry::BrowsedPathEntry(const BrowseCallback& browse) :
|
||||
m_entry(PathEntry_new()),
|
||||
m_browse(browse)
|
||||
{
|
||||
g_signal_connect(G_OBJECT(m_entry.m_button), "clicked", G_CALLBACK(BrowsedPathEntry_clicked), this);
|
||||
}
|
||||
|
||||
|
||||
GtkLabel* DialogLabel_new(const char* name)
|
||||
{
|
||||
GtkLabel* label = GTK_LABEL(gtk_label_new(name));
|
||||
gtk_widget_show(GTK_WIDGET(label));
|
||||
gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
|
||||
gtk_label_set_justify(label, GTK_JUSTIFY_LEFT);
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
GtkTable* DialogRow_new(const char* name, GtkWidget* widget)
|
||||
{
|
||||
GtkTable* table = GTK_TABLE(gtk_table_new(1, 3, TRUE));
|
||||
gtk_widget_show(GTK_WIDGET(table));
|
||||
|
||||
gtk_table_set_col_spacings(table, 4);
|
||||
gtk_table_set_row_spacings(table, 0);
|
||||
|
||||
gtk_table_attach(table, GTK_WIDGET(DialogLabel_new(name)), 0, 1, 0, 1,
|
||||
(GtkAttachOptions) (GTK_EXPAND|GTK_FILL),
|
||||
(GtkAttachOptions) (0), 0, 0);
|
||||
|
||||
gtk_table_attach(table, widget, 1, 3, 0, 1,
|
||||
(GtkAttachOptions) (GTK_EXPAND|GTK_FILL),
|
||||
(GtkAttachOptions) (0), 0, 0);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
void DialogVBox_packRow(GtkVBox* vbox, GtkWidget* row)
|
||||
{
|
||||
gtk_box_pack_start(GTK_BOX(vbox), row, FALSE, FALSE, 0);
|
||||
}
|
||||
|
125
tools/urt/libs/gtkutil/dialog.h
Normal file
125
tools/urt/libs/gtkutil/dialog.h
Normal file
|
@ -0,0 +1,125 @@
|
|||
|
||||
#if !defined(INCLUDED_GTKUTIL_DIALOG_H)
|
||||
#define INCLUDED_GTKUTIL_DIALOG_H
|
||||
|
||||
#include "generic/callback.h"
|
||||
#include "generic/arrayrange.h"
|
||||
#include "qerplugin.h"
|
||||
#include <gtk/gtkenums.h>
|
||||
|
||||
typedef int gint;
|
||||
typedef gint gboolean;
|
||||
typedef struct _GdkEventAny GdkEventAny;
|
||||
typedef struct _GtkWidget GtkWidget;
|
||||
typedef struct _GtkHBox GtkHBox;
|
||||
typedef struct _GtkVBox GtkVBox;
|
||||
typedef struct _GtkRadioButton GtkRadioButton;
|
||||
typedef struct _GtkFrame GtkFrame;
|
||||
typedef struct _GtkEntry GtkEntry;
|
||||
typedef struct _GtkButton GtkButton;
|
||||
typedef struct _GtkLabel GtkLabel;
|
||||
typedef struct _GtkTable GtkTable;
|
||||
|
||||
|
||||
struct ModalDialog
|
||||
{
|
||||
ModalDialog()
|
||||
: loop(true), ret(eIDCANCEL)
|
||||
{
|
||||
}
|
||||
bool loop;
|
||||
EMessageBoxReturn ret;
|
||||
};
|
||||
|
||||
struct ModalDialogButton
|
||||
{
|
||||
ModalDialogButton(ModalDialog& dialog, EMessageBoxReturn value)
|
||||
: m_dialog(dialog), m_value(value)
|
||||
{
|
||||
}
|
||||
ModalDialog& m_dialog;
|
||||
EMessageBoxReturn m_value;
|
||||
};
|
||||
|
||||
typedef void (*GCallback)(void);
|
||||
typedef void* gpointer;
|
||||
typedef struct _GtkWindow GtkWindow;
|
||||
typedef struct _GtkTable GtkTable;
|
||||
typedef struct _GtkButton GtkButton;
|
||||
typedef struct _GtkVBox GtkVBox;
|
||||
typedef struct _GtkHBox GtkHBox;
|
||||
typedef struct _GtkFrame GtkFrame;
|
||||
|
||||
GtkWindow* create_fixedsize_modal_window(GtkWindow* parent, const char* title, int width, int height);
|
||||
|
||||
GtkWindow* create_dialog_window(GtkWindow* parent, const char* title, GCallback func, gpointer data, int default_w = -1, int default_h = -1);
|
||||
GtkTable* create_dialog_table(unsigned int rows, unsigned int columns, unsigned int row_spacing, unsigned int col_spacing, int border = 0);
|
||||
GtkButton* create_dialog_button(const char* label, GCallback func, gpointer data);
|
||||
GtkVBox* create_dialog_vbox(int spacing, int border = 0);
|
||||
GtkHBox* create_dialog_hbox(int spacing, int border = 0);
|
||||
GtkFrame* create_dialog_frame(const char* label, GtkShadowType shadow = GTK_SHADOW_ETCHED_IN);
|
||||
|
||||
GtkButton* create_modal_dialog_button(const char* label, ModalDialogButton& button);
|
||||
GtkWindow* create_modal_dialog_window(GtkWindow* parent, const char* title, ModalDialog& dialog, int default_w = -1, int default_h = -1);
|
||||
GtkWindow* create_fixedsize_modal_dialog_window(GtkWindow* parent, const char* title, ModalDialog& dialog, int width = -1, int height = -1);
|
||||
EMessageBoxReturn modal_dialog_show(GtkWindow* window, ModalDialog& dialog);
|
||||
|
||||
|
||||
gboolean dialog_button_ok(GtkWidget *widget, ModalDialog* data);
|
||||
gboolean dialog_button_cancel(GtkWidget *widget, ModalDialog* data);
|
||||
gboolean dialog_button_yes(GtkWidget *widget, ModalDialog* data);
|
||||
gboolean dialog_button_no(GtkWidget *widget, ModalDialog* data);
|
||||
gboolean dialog_delete_callback(GtkWidget *widget, GdkEventAny* event, ModalDialog* data);
|
||||
|
||||
GtkWindow* create_simple_modal_dialog_window(const char* title, ModalDialog& dialog, GtkWidget* contents);
|
||||
|
||||
class RadioHBox
|
||||
{
|
||||
public:
|
||||
GtkHBox* m_hbox;
|
||||
GtkRadioButton* m_radio;
|
||||
RadioHBox(GtkHBox* hbox, GtkRadioButton* radio) :
|
||||
m_hbox(hbox),
|
||||
m_radio(radio)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
RadioHBox RadioHBox_new(StringArrayRange names);
|
||||
|
||||
|
||||
class PathEntry
|
||||
{
|
||||
public:
|
||||
GtkFrame* m_frame;
|
||||
GtkEntry* m_entry;
|
||||
GtkButton* m_button;
|
||||
PathEntry(GtkFrame* frame, GtkEntry* entry, GtkButton* button) :
|
||||
m_frame(frame),
|
||||
m_entry(entry),
|
||||
m_button(button)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
PathEntry PathEntry_new();
|
||||
|
||||
class BrowsedPathEntry
|
||||
{
|
||||
public:
|
||||
typedef Callback1<const char*> SetPathCallback;
|
||||
typedef Callback1<const SetPathCallback&> BrowseCallback;
|
||||
|
||||
PathEntry m_entry;
|
||||
BrowseCallback m_browse;
|
||||
|
||||
BrowsedPathEntry(const BrowseCallback& browse);
|
||||
};
|
||||
|
||||
GtkLabel* DialogLabel_new(const char* name);
|
||||
GtkTable* DialogRow_new(const char* name, GtkWidget* widget);
|
||||
typedef struct _GtkVBox GtkVBox;
|
||||
void DialogVBox_packRow(GtkVBox* vbox, GtkWidget* row);
|
||||
|
||||
|
||||
#endif
|
3
tools/urt/libs/gtkutil/entry.cpp
Normal file
3
tools/urt/libs/gtkutil/entry.cpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
#include "entry.h"
|
||||
|
43
tools/urt/libs/gtkutil/entry.h
Normal file
43
tools/urt/libs/gtkutil/entry.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
|
||||
#if !defined(INCLUDED_GTKUTIL_ENTRY_H)
|
||||
#define INCLUDED_GTKUTIL_ENTRY_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <gtk/gtkentry.h>
|
||||
|
||||
inline void entry_set_string(GtkEntry* entry, const char* string)
|
||||
{
|
||||
gtk_entry_set_text(entry, string);
|
||||
}
|
||||
|
||||
inline void entry_set_int(GtkEntry* entry, int i)
|
||||
{
|
||||
char buf[32];
|
||||
sprintf(buf, "%d", i);
|
||||
entry_set_string(entry, buf);
|
||||
}
|
||||
|
||||
inline void entry_set_float(GtkEntry* entry, float f)
|
||||
{
|
||||
char buf[32];
|
||||
sprintf(buf, "%g", f);
|
||||
entry_set_string(entry, buf);
|
||||
}
|
||||
|
||||
inline const char* entry_get_string(GtkEntry* entry)
|
||||
{
|
||||
return gtk_entry_get_text(entry);
|
||||
}
|
||||
|
||||
inline int entry_get_int(GtkEntry* entry)
|
||||
{
|
||||
return atoi(entry_get_string(entry));
|
||||
}
|
||||
|
||||
inline double entry_get_float(GtkEntry* entry)
|
||||
{
|
||||
return atof(entry_get_string(entry));
|
||||
}
|
||||
|
||||
#endif
|
463
tools/urt/libs/gtkutil/filechooser.cpp
Normal file
463
tools/urt/libs/gtkutil/filechooser.cpp
Normal file
|
@ -0,0 +1,463 @@
|
|||
|
||||
#include "filechooser.h"
|
||||
|
||||
#include "ifiletypes.h"
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <gtk/gtkwidget.h>
|
||||
#include <gtk/gtkwindow.h>
|
||||
#include <gtk/gtkfilechooser.h>
|
||||
#include <gtk/gtkfilechooserdialog.h>
|
||||
#include <gtk/gtkstock.h>
|
||||
|
||||
#include "string/string.h"
|
||||
#include "stream/stringstream.h"
|
||||
#include "container/array.h"
|
||||
#include "os/path.h"
|
||||
#include "os/file.h"
|
||||
|
||||
#include "messagebox.h"
|
||||
|
||||
|
||||
struct filetype_pair_t
|
||||
{
|
||||
filetype_pair_t()
|
||||
: m_moduleName("")
|
||||
{
|
||||
}
|
||||
filetype_pair_t(const char* moduleName, filetype_t type)
|
||||
: m_moduleName(moduleName), m_type(type)
|
||||
{
|
||||
}
|
||||
const char* m_moduleName;
|
||||
filetype_t m_type;
|
||||
};
|
||||
|
||||
class FileTypeList : public IFileTypeList
|
||||
{
|
||||
struct filetype_copy_t
|
||||
{
|
||||
filetype_copy_t(const filetype_pair_t& other)
|
||||
: m_moduleName(other.m_moduleName), m_name(other.m_type.name), m_pattern(other.m_type.pattern)
|
||||
{
|
||||
}
|
||||
CopiedString m_moduleName;
|
||||
CopiedString m_name;
|
||||
CopiedString m_pattern;
|
||||
};
|
||||
|
||||
typedef std::list<filetype_copy_t> Types;
|
||||
Types m_types;
|
||||
public:
|
||||
|
||||
typedef Types::const_iterator const_iterator;
|
||||
const_iterator begin() const
|
||||
{
|
||||
return m_types.begin();
|
||||
}
|
||||
const_iterator end() const
|
||||
{
|
||||
return m_types.end();
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
return m_types.size();
|
||||
}
|
||||
|
||||
void addType(const char* moduleName, filetype_t type)
|
||||
{
|
||||
m_types.push_back(filetype_pair_t(moduleName, type));
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
class Win32Filters
|
||||
{
|
||||
const FileTypeList& m_types;
|
||||
Array<char> m_filters;
|
||||
public:
|
||||
Win32Filters(const FileTypeList& typeList) : m_types(typeList)
|
||||
{
|
||||
std::size_t len = 0;
|
||||
for(FileTypeList::const_iterator i = m_types.begin(); i != m_types.end(); ++i)
|
||||
{
|
||||
len = len + strlen((*i).m_name.c_str()) + strlen((*i).m_pattern.c_str()) * 2 + 5;
|
||||
}
|
||||
m_filters.resize(len + 1); // length + null char
|
||||
char *w = m_filters.data();
|
||||
for(FileTypeList::const_iterator i = m_types.begin(); i != m_types.end(); ++i)
|
||||
{
|
||||
for(const char *r = (*i).m_name.c_str(); *r!='\0'; r++, w++)
|
||||
{
|
||||
*w = *r;
|
||||
}
|
||||
*w++ = ' ';
|
||||
*w++ = '(';
|
||||
for(const char *r = (*i).m_pattern.c_str(); *r!='\0'; r++, w++)
|
||||
{
|
||||
*w = *r;
|
||||
}
|
||||
*w++ = ')';
|
||||
*w++ = '\0';
|
||||
for(const char *r = (*i).m_pattern.c_str(); *r!='\0'; r++, w++)
|
||||
{
|
||||
*w = (*r == ',') ? ';' : *r;
|
||||
}
|
||||
*w++ = '\0';
|
||||
}
|
||||
m_filters[len] = '\0';
|
||||
}
|
||||
filetype_pair_t getType(const char *filter) const
|
||||
{
|
||||
for(FileTypeList::const_iterator i = m_types.begin(); i != m_types.end(); ++i)
|
||||
{
|
||||
if(string_equal((*i).m_pattern.c_str(), filter))
|
||||
{
|
||||
return filetype_pair_t((*i).m_moduleName.c_str(), filetype_t((*i).m_name.c_str(), (*i).m_pattern.c_str()));
|
||||
}
|
||||
}
|
||||
return filetype_pair_t();
|
||||
}
|
||||
const char* getFilters() const
|
||||
{
|
||||
return m_filters.data();
|
||||
}
|
||||
};
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <gdk/gdkwin32.h>
|
||||
#include <commdlg.h>
|
||||
|
||||
static char szFile[MAX_PATH]; /* filename string */
|
||||
|
||||
|
||||
#define FILEDLG_CUSTOM_FILTER_LENGTH 64
|
||||
// to be used with the advanced file selector
|
||||
|
||||
const char* file_dialog_show_win32(GtkWidget* parent, bool open, const char* title, const char* path, const char* pattern)
|
||||
{
|
||||
const char* r;
|
||||
char* w;
|
||||
filetype_t type;
|
||||
FileTypeList typelist;
|
||||
|
||||
if(pattern == 0)
|
||||
{
|
||||
pattern = "*";
|
||||
}
|
||||
|
||||
GlobalFiletypes().getTypeList(pattern, &typelist);
|
||||
|
||||
Win32Filters filters(typelist);
|
||||
|
||||
// win32 dialog stores the selected "save as type" extension in the second null-terminated string
|
||||
char customfilter[FILEDLG_CUSTOM_FILTER_LENGTH];
|
||||
|
||||
static OPENFILENAME ofn; /* common dialog box structure */
|
||||
static char szDirName[MAX_PATH]; /* directory string */
|
||||
static char szFile[MAX_PATH]; /* filename string */
|
||||
static char szFileTitle[MAX_PATH]; /* file title string */
|
||||
static int i, cbString; /* integer count variables */
|
||||
static HANDLE hf; /* file handle */
|
||||
|
||||
// do that the native way
|
||||
/* Place the terminating null character in the szFile. */
|
||||
szFile[0] = '\0';
|
||||
customfilter[0] = customfilter[1] = customfilter[2] = '\0';
|
||||
|
||||
/* Set the members of the OPENFILENAME structure. */
|
||||
ofn.lStructSize = sizeof(OPENFILENAME);
|
||||
ofn.hwndOwner = (HWND)GDK_WINDOW_HWND(parent->window);
|
||||
ofn.nFilterIndex = 0;
|
||||
ofn.lpstrFilter = filters.getFilters();
|
||||
ofn.lpstrCustomFilter = customfilter;
|
||||
ofn.nMaxCustFilter = sizeof(customfilter);
|
||||
ofn.lpstrFile = szFile;
|
||||
ofn.nMaxFile = sizeof(szFile);
|
||||
ofn.lpstrFileTitle = 0; // we don't need to get the name of the file
|
||||
if(path)
|
||||
{
|
||||
// szDirName: Radiant uses unix convention for paths internally
|
||||
// Win32 (of course) and Gtk (who would have thought) expect the '\\' convention
|
||||
// copy path, replacing dir separators as appropriate
|
||||
for(r=path, w=szDirName; *r!='\0'; r++)
|
||||
*w++ = (*r=='/') ? '\\' : *r;
|
||||
// terminate string
|
||||
*w = '\0';
|
||||
ofn.lpstrInitialDir = szDirName;
|
||||
}
|
||||
else ofn.lpstrInitialDir = 0;
|
||||
ofn.lpstrTitle = title;
|
||||
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
|
||||
|
||||
/* Display the Open dialog box. */
|
||||
// it's open or close depending on 'open' parameter
|
||||
if (open)
|
||||
{
|
||||
if (!GetOpenFileName(&ofn))
|
||||
return 0; // canceled
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!GetSaveFileName(&ofn))
|
||||
return 0; // canceled
|
||||
}
|
||||
|
||||
if(!string_equal(pattern, "*"))
|
||||
{
|
||||
type = filters.getType(customfilter+1).m_type;
|
||||
}
|
||||
|
||||
// don't return an empty filename
|
||||
if(szFile[0] == '\0') return 0;
|
||||
|
||||
// convert back to unix format
|
||||
for(w=szFile; *w!='\0'; w++)
|
||||
{
|
||||
if(*w=='\\')
|
||||
{
|
||||
*w = '/';
|
||||
}
|
||||
}
|
||||
// when saving, force an extension depending on filetype
|
||||
/* \todo SPoG - file_dialog should return filetype information separately.. not force file extension.. */
|
||||
if(!open && !string_equal(pattern, "*"))
|
||||
{
|
||||
// last ext separator
|
||||
const char* extension = path_get_extension(szFile);
|
||||
// no extension
|
||||
if(string_empty(extension))
|
||||
{
|
||||
strcat(szFile, type.pattern+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(szFile + (extension - szFile), type.pattern+2);
|
||||
}
|
||||
}
|
||||
|
||||
return szFile;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
class GTKMasks
|
||||
{
|
||||
const FileTypeList& m_types;
|
||||
public:
|
||||
std::vector<CopiedString> m_filters;
|
||||
std::vector<CopiedString> m_masks;
|
||||
|
||||
GTKMasks(const FileTypeList& types) : m_types(types)
|
||||
{
|
||||
m_masks.reserve(m_types.size());
|
||||
for(FileTypeList::const_iterator i = m_types.begin(); i != m_types.end(); ++i)
|
||||
{
|
||||
std::size_t len = strlen((*i).m_name.c_str()) + strlen((*i).m_pattern.c_str()) + 3;
|
||||
StringOutputStream buffer(len + 1); // length + null char
|
||||
|
||||
buffer << (*i).m_name.c_str() << " <" << (*i).m_pattern.c_str() << ">";
|
||||
|
||||
m_masks.push_back(buffer.c_str());
|
||||
}
|
||||
|
||||
m_filters.reserve(m_types.size());
|
||||
for(FileTypeList::const_iterator i = m_types.begin(); i != m_types.end(); ++i)
|
||||
{
|
||||
m_filters.push_back((*i).m_pattern);
|
||||
}
|
||||
}
|
||||
|
||||
filetype_pair_t GetTypeForGTKMask(const char *mask) const
|
||||
{
|
||||
std::vector<CopiedString>::const_iterator j = m_masks.begin();
|
||||
for(FileTypeList::const_iterator i = m_types.begin(); i != m_types.end(); ++i, ++j)
|
||||
{
|
||||
if(string_equal((*j).c_str(), mask))
|
||||
{
|
||||
return filetype_pair_t((*i).m_moduleName.c_str(), filetype_t((*i).m_name.c_str(), (*i).m_pattern.c_str()));
|
||||
}
|
||||
}
|
||||
return filetype_pair_t();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static char g_file_dialog_file[1024];
|
||||
|
||||
const char* file_dialog_show(GtkWidget* parent, bool open, const char* title, const char* path, const char* pattern)
|
||||
{
|
||||
filetype_t type;
|
||||
|
||||
if(pattern == 0)
|
||||
{
|
||||
pattern = "*";
|
||||
}
|
||||
|
||||
FileTypeList typelist;
|
||||
GlobalFiletypes().getTypeList(pattern, &typelist);
|
||||
|
||||
GTKMasks masks(typelist);
|
||||
|
||||
if (title == 0)
|
||||
title = open ? "Open File" : "Save File";
|
||||
|
||||
GtkWidget* dialog;
|
||||
if (open)
|
||||
{
|
||||
dialog = gtk_file_chooser_dialog_new(title,
|
||||
GTK_WINDOW(parent),
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||||
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
dialog = gtk_file_chooser_dialog_new(title,
|
||||
GTK_WINDOW(parent),
|
||||
GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||||
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "unnamed");
|
||||
}
|
||||
|
||||
gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
|
||||
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
|
||||
|
||||
// we expect an actual path below, if the path is 0 we might crash
|
||||
if (path != 0 && !string_empty(path))
|
||||
{
|
||||
ASSERT_MESSAGE(path_is_absolute(path), "file_dialog_show: path not absolute: " << makeQuoted(path));
|
||||
|
||||
Array<char> new_path(strlen(path)+1);
|
||||
|
||||
// copy path, replacing dir separators as appropriate
|
||||
Array<char>::iterator w = new_path.begin();
|
||||
for(const char* r = path; *r != '\0'; ++r)
|
||||
{
|
||||
*w++ = (*r == '/') ? G_DIR_SEPARATOR : *r;
|
||||
}
|
||||
// remove separator from end of path if required
|
||||
if(*(w-1) == G_DIR_SEPARATOR)
|
||||
{
|
||||
--w;
|
||||
}
|
||||
// terminate string
|
||||
*w = '\0';
|
||||
|
||||
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), new_path.data());
|
||||
}
|
||||
|
||||
// we should add all important paths as shortcut folder...
|
||||
// gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog), "/tmp/", NULL);
|
||||
|
||||
|
||||
for(std::size_t i = 0; i < masks.m_filters.size(); ++i)
|
||||
{
|
||||
GtkFileFilter* filter = gtk_file_filter_new();
|
||||
gtk_file_filter_add_pattern(filter, masks.m_filters[i].c_str());
|
||||
gtk_file_filter_set_name(filter, masks.m_masks[i].c_str());
|
||||
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
|
||||
}
|
||||
|
||||
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
strcpy(g_file_dialog_file, gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)));
|
||||
|
||||
if(!string_equal(pattern, "*"))
|
||||
{
|
||||
GtkFileFilter* filter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(dialog));
|
||||
type = masks.GetTypeForGTKMask(gtk_file_filter_get_name(filter)).m_type;
|
||||
// last ext separator
|
||||
const char* extension = path_get_extension(g_file_dialog_file);
|
||||
// no extension
|
||||
if(string_empty(extension))
|
||||
{
|
||||
strcat(g_file_dialog_file, type.pattern+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(g_file_dialog_file + (extension - g_file_dialog_file), type.pattern+2);
|
||||
}
|
||||
}
|
||||
|
||||
// convert back to unix format
|
||||
for(char* w = g_file_dialog_file; *w!='\0'; w++)
|
||||
{
|
||||
if(*w=='\\')
|
||||
{
|
||||
*w = '/';
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_file_dialog_file[0] = '\0';
|
||||
}
|
||||
|
||||
gtk_widget_destroy(dialog);
|
||||
|
||||
// don't return an empty filename
|
||||
if(g_file_dialog_file[0] == '\0') return NULL;
|
||||
|
||||
return g_file_dialog_file;
|
||||
}
|
||||
|
||||
char* dir_dialog(GtkWidget* parent, const char* title, const char* path)
|
||||
{
|
||||
GtkWidget* dialog = gtk_file_chooser_dialog_new(title,
|
||||
GTK_WINDOW(parent),
|
||||
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||||
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
|
||||
gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
|
||||
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
|
||||
|
||||
if(!string_empty(path))
|
||||
{
|
||||
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path);
|
||||
}
|
||||
|
||||
char* filename = 0;
|
||||
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
|
||||
}
|
||||
|
||||
gtk_widget_destroy(dialog);
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
bool g_FileChooser_nativeGUI = true;
|
||||
#endif
|
||||
|
||||
const char* file_dialog (GtkWidget* parent, bool open, const char* title, const char* path, const char* pattern)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
const char* file =
|
||||
#ifdef WIN32
|
||||
g_FileChooser_nativeGUI
|
||||
? file_dialog_show_win32(parent, open, title, path, pattern) :
|
||||
#endif
|
||||
file_dialog_show(parent, open, title, path, pattern);
|
||||
|
||||
if(open
|
||||
|| !file_exists(file)
|
||||
|| gtk_MessageBox(parent, "The file specified already exists.\nDo you want to replace it?", title, eMB_NOYES, eMB_ICONQUESTION) == eIDYES)
|
||||
{
|
||||
return file;
|
||||
}
|
||||
}
|
||||
}
|
22
tools/urt/libs/gtkutil/filechooser.h
Normal file
22
tools/urt/libs/gtkutil/filechooser.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
|
||||
#if !defined(INCLUDED_GTKUTIL_FILECHOOSER_H)
|
||||
#define INCLUDED_GTKUTIL_FILECHOOSER_H
|
||||
|
||||
/// \file
|
||||
/// GTK+ file-chooser dialogs.
|
||||
|
||||
#ifdef WIN32
|
||||
extern bool g_FileChooser_nativeGUI;
|
||||
#endif
|
||||
|
||||
typedef struct _GtkWidget GtkWidget;
|
||||
const char* file_dialog(GtkWidget *parent, bool open, const char* title, const char* path = 0, const char* pattern = 0);
|
||||
|
||||
|
||||
/// \brief Prompts the user to browse for a directory.
|
||||
/// The prompt window will be transient to \p parent.
|
||||
/// The directory will initially default to \p path, which must be an absolute path.
|
||||
/// The returned string is allocated with \c g_malloc and must be freed with \c g_free.
|
||||
char* dir_dialog(GtkWidget *parent, const char* title = "Choose Directory", const char* path = "");
|
||||
|
||||
#endif
|
15
tools/urt/libs/gtkutil/frame.cpp
Normal file
15
tools/urt/libs/gtkutil/frame.cpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
|
||||
#include "frame.h"
|
||||
|
||||
#include <gtk/gtkframe.h>
|
||||
|
||||
GtkFrame* create_framed_widget(GtkWidget* widget)
|
||||
{
|
||||
GtkFrame* frame = GTK_FRAME(gtk_frame_new(0));
|
||||
gtk_widget_show(GTK_WIDGET(frame));
|
||||
gtk_frame_set_shadow_type(frame, GTK_SHADOW_IN);
|
||||
gtk_container_add (GTK_CONTAINER(frame), widget);
|
||||
gtk_widget_show(GTK_WIDGET(widget));
|
||||
return frame;
|
||||
}
|
||||
|
9
tools/urt/libs/gtkutil/frame.h
Normal file
9
tools/urt/libs/gtkutil/frame.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
#if !defined(INCLUDED_GTKUTIL_FRAME_H)
|
||||
#define INCLUDED_GTKUTIL_FRAME_H
|
||||
|
||||
typedef struct _GtkWidget GtkWidget;
|
||||
typedef struct _GtkFrame GtkFrame;
|
||||
GtkFrame* create_framed_widget(GtkWidget* widget);
|
||||
|
||||
#endif
|
36
tools/urt/libs/gtkutil/glfont.cpp
Normal file
36
tools/urt/libs/gtkutil/glfont.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
|
||||
#include "glfont.h"
|
||||
|
||||
#include "igl.h"
|
||||
#include <gtk/gtkglwidget.h>
|
||||
|
||||
GLFont glfont_create(const char* font_string)
|
||||
{
|
||||
GLuint font_list_base = glGenLists (256);
|
||||
gint font_height = 0;
|
||||
|
||||
PangoFontDescription* font_desc = pango_font_description_from_string (font_string);
|
||||
|
||||
PangoFont* font = gdk_gl_font_use_pango_font (font_desc, 0, 256, font_list_base);
|
||||
|
||||
if(font != 0)
|
||||
{
|
||||
PangoFontMetrics* font_metrics = pango_font_get_metrics (font, 0);
|
||||
|
||||
font_height = pango_font_metrics_get_ascent (font_metrics) +
|
||||
pango_font_metrics_get_descent (font_metrics);
|
||||
font_height = PANGO_PIXELS (font_height);
|
||||
|
||||
pango_font_metrics_unref (font_metrics);
|
||||
}
|
||||
|
||||
pango_font_description_free (font_desc);
|
||||
|
||||
return GLFont(font_list_base, font_height);
|
||||
}
|
||||
|
||||
void glfont_release(GLFont& font)
|
||||
{
|
||||
glDeleteLists(font.getDisplayList(), 256);
|
||||
font = GLFont(0, 0);
|
||||
}
|
28
tools/urt/libs/gtkutil/glfont.h
Normal file
28
tools/urt/libs/gtkutil/glfont.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
|
||||
#if !defined(INCLUDED_GTKUTIL_GLFONT_H)
|
||||
#define INCLUDED_GTKUTIL_GLFONT_H
|
||||
|
||||
typedef unsigned int GLuint;
|
||||
|
||||
class GLFont
|
||||
{
|
||||
GLuint m_displayList;
|
||||
int m_pixelHeight;
|
||||
public:
|
||||
GLFont(GLuint displayList, int pixelHeight) : m_displayList(displayList), m_pixelHeight(pixelHeight)
|
||||
{
|
||||
}
|
||||
GLuint getDisplayList() const
|
||||
{
|
||||
return m_displayList;
|
||||
}
|
||||
int getPixelHeight() const
|
||||
{
|
||||
return m_pixelHeight;
|
||||
}
|
||||
};
|
||||
|
||||
GLFont glfont_create(const char* font_string);
|
||||
void glfont_release(GLFont& font);
|
||||
|
||||
#endif
|
254
tools/urt/libs/gtkutil/glwidget.cpp
Normal file
254
tools/urt/libs/gtkutil/glwidget.cpp
Normal file
|
@ -0,0 +1,254 @@
|
|||
|
||||
// OpenGL widget based on GtkGLExt
|
||||
|
||||
#include "glwidget.h"
|
||||
|
||||
#include "debugging/debugging.h"
|
||||
|
||||
#include "igl.h"
|
||||
|
||||
#include <gtk/gtkdrawingarea.h>
|
||||
#include <gtk/gtkglwidget.h>
|
||||
|
||||
#include "pointer.h"
|
||||
|
||||
void (*GLWidget_sharedContextCreated)() = 0;
|
||||
void (*GLWidget_sharedContextDestroyed)() = 0;
|
||||
|
||||
|
||||
typedef int* attribs_t;
|
||||
struct config_t
|
||||
{
|
||||
const char* name;
|
||||
attribs_t attribs;
|
||||
};
|
||||
typedef const config_t* configs_iterator;
|
||||
|
||||
int config_rgba32[] = {
|
||||
GDK_GL_RGBA,
|
||||
GDK_GL_DOUBLEBUFFER,
|
||||
GDK_GL_BUFFER_SIZE, 24,
|
||||
GDK_GL_ATTRIB_LIST_NONE,
|
||||
};
|
||||
|
||||
int config_rgba[] = {
|
||||
GDK_GL_RGBA,
|
||||
GDK_GL_DOUBLEBUFFER,
|
||||
GDK_GL_BUFFER_SIZE, 16,
|
||||
GDK_GL_ATTRIB_LIST_NONE,
|
||||
};
|
||||
|
||||
const config_t configs[] = {
|
||||
{
|
||||
"colour-buffer = 32bpp, depth-buffer = none",
|
||||
config_rgba32,
|
||||
},
|
||||
{
|
||||
"colour-buffer = 16bpp, depth-buffer = none",
|
||||
config_rgba,
|
||||
}
|
||||
};
|
||||
|
||||
GdkGLConfig* glconfig_new()
|
||||
{
|
||||
GdkGLConfig* glconfig = 0;
|
||||
|
||||
for(configs_iterator i = configs, end = configs + 2; i != end; ++i)
|
||||
{
|
||||
glconfig = gdk_gl_config_new((*i).attribs);
|
||||
if(glconfig != 0)
|
||||
{
|
||||
globalOutputStream() << "OpenGL window configuration: " << (*i).name << "\n";
|
||||
return glconfig;
|
||||
}
|
||||
}
|
||||
|
||||
globalOutputStream() << "OpenGL window configuration: colour-buffer = auto, depth-buffer = none\n";
|
||||
return gdk_gl_config_new_by_mode((GdkGLConfigMode)(GDK_GL_MODE_RGBA | GDK_GL_MODE_DOUBLE));
|
||||
}
|
||||
|
||||
int config_rgba32_depth32[] = {
|
||||
GDK_GL_RGBA,
|
||||
GDK_GL_DOUBLEBUFFER,
|
||||
GDK_GL_BUFFER_SIZE, 24,
|
||||
GDK_GL_DEPTH_SIZE, 32,
|
||||
GDK_GL_ATTRIB_LIST_NONE,
|
||||
};
|
||||
|
||||
int config_rgba32_depth24[] = {
|
||||
GDK_GL_RGBA,
|
||||
GDK_GL_DOUBLEBUFFER,
|
||||
GDK_GL_BUFFER_SIZE, 24,
|
||||
GDK_GL_DEPTH_SIZE, 24,
|
||||
GDK_GL_ATTRIB_LIST_NONE,
|
||||
};
|
||||
|
||||
int config_rgba32_depth16[] = {
|
||||
GDK_GL_RGBA,
|
||||
GDK_GL_DOUBLEBUFFER,
|
||||
GDK_GL_BUFFER_SIZE, 24,
|
||||
GDK_GL_DEPTH_SIZE, 16,
|
||||
GDK_GL_ATTRIB_LIST_NONE,
|
||||
};
|
||||
|
||||
int config_rgba32_depth[] = {
|
||||
GDK_GL_RGBA,
|
||||
GDK_GL_DOUBLEBUFFER,
|
||||
GDK_GL_BUFFER_SIZE, 24,
|
||||
GDK_GL_DEPTH_SIZE, 1,
|
||||
GDK_GL_ATTRIB_LIST_NONE,
|
||||
};
|
||||
|
||||
int config_rgba_depth16[] = {
|
||||
GDK_GL_RGBA,
|
||||
GDK_GL_DOUBLEBUFFER,
|
||||
GDK_GL_BUFFER_SIZE, 16,
|
||||
GDK_GL_DEPTH_SIZE, 16,
|
||||
GDK_GL_ATTRIB_LIST_NONE,
|
||||
};
|
||||
|
||||
int config_rgba_depth[] = {
|
||||
GDK_GL_RGBA,
|
||||
GDK_GL_DOUBLEBUFFER,
|
||||
GDK_GL_BUFFER_SIZE, 16,
|
||||
GDK_GL_DEPTH_SIZE, 1,
|
||||
GDK_GL_ATTRIB_LIST_NONE,
|
||||
};
|
||||
|
||||
const config_t configs_with_depth[] =
|
||||
{
|
||||
{
|
||||
"colour-buffer = 32bpp, depth-buffer = 32bpp",
|
||||
config_rgba32_depth32,
|
||||
},
|
||||
{
|
||||
"colour-buffer = 32bpp, depth-buffer = 24bpp",
|
||||
config_rgba32_depth24,
|
||||
},
|
||||
{
|
||||
"colour-buffer = 32bpp, depth-buffer = 16bpp",
|
||||
config_rgba32_depth16,
|
||||
},
|
||||
{
|
||||
"colour-buffer = 32bpp, depth-buffer = auto",
|
||||
config_rgba32_depth,
|
||||
},
|
||||
{
|
||||
"colour-buffer = 16bpp, depth-buffer = 16bpp",
|
||||
config_rgba_depth16,
|
||||
},
|
||||
{
|
||||
"colour-buffer = auto, depth-buffer = auto",
|
||||
config_rgba_depth,
|
||||
},
|
||||
};
|
||||
|
||||
GdkGLConfig* glconfig_new_with_depth()
|
||||
{
|
||||
GdkGLConfig* glconfig = 0;
|
||||
|
||||
for(configs_iterator i = configs_with_depth, end = configs_with_depth + 6; i != end; ++i)
|
||||
{
|
||||
glconfig = gdk_gl_config_new((*i).attribs);
|
||||
if(glconfig != 0)
|
||||
{
|
||||
globalOutputStream() << "OpenGL window configuration: " << (*i).name << "\n";
|
||||
return glconfig;
|
||||
}
|
||||
}
|
||||
|
||||
globalOutputStream() << "OpenGL window configuration: colour-buffer = auto, depth-buffer = auto (fallback)\n";
|
||||
return gdk_gl_config_new_by_mode((GdkGLConfigMode)(GDK_GL_MODE_RGBA | GDK_GL_MODE_DOUBLE | GDK_GL_MODE_DEPTH));
|
||||
}
|
||||
|
||||
unsigned int g_context_count = 0;
|
||||
|
||||
namespace
|
||||
{
|
||||
GtkWidget* g_shared = 0;
|
||||
}
|
||||
|
||||
gint glwidget_context_created(GtkWidget* widget, gpointer data)
|
||||
{
|
||||
if(++g_context_count == 1)
|
||||
{
|
||||
g_shared = widget;
|
||||
gtk_widget_ref(g_shared);
|
||||
|
||||
glwidget_make_current(g_shared);
|
||||
GlobalOpenGL().contextValid = true;
|
||||
|
||||
GLWidget_sharedContextCreated();
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gint glwidget_context_destroyed(GtkWidget* widget, gpointer data)
|
||||
{
|
||||
if(--g_context_count == 0)
|
||||
{
|
||||
GlobalOpenGL().contextValid = false;
|
||||
|
||||
GLWidget_sharedContextDestroyed();
|
||||
|
||||
gtk_widget_unref(g_shared);
|
||||
g_shared = 0;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean glwidget_enable_gl(GtkWidget* widget, GtkWidget* widget2, gpointer data)
|
||||
{
|
||||
if(widget2 == 0 && !gtk_widget_is_gl_capable(widget))
|
||||
{
|
||||
GdkGLConfig* glconfig = (g_object_get_data(G_OBJECT(widget), "zbuffer")) ? glconfig_new_with_depth() : glconfig_new();
|
||||
ASSERT_MESSAGE(glconfig != 0, "failed to create OpenGL config");
|
||||
|
||||
gtk_widget_set_gl_capability(widget, glconfig, g_shared != 0 ? gtk_widget_get_gl_context(g_shared) : 0, TRUE, GDK_GL_RGBA_TYPE);
|
||||
|
||||
gtk_widget_realize(widget);
|
||||
if(g_shared == 0)
|
||||
{
|
||||
g_shared = widget;
|
||||
}
|
||||
|
||||
// free glconfig?
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GtkWidget* glwidget_new(gboolean zbuffer)
|
||||
{
|
||||
GtkWidget* widget = gtk_drawing_area_new();
|
||||
|
||||
g_object_set_data(G_OBJECT(widget), "zbuffer", gint_to_pointer(zbuffer));
|
||||
|
||||
g_signal_connect(G_OBJECT(widget), "hierarchy-changed", G_CALLBACK(glwidget_enable_gl), 0);
|
||||
|
||||
g_signal_connect(G_OBJECT(widget), "realize", G_CALLBACK(glwidget_context_created), 0);
|
||||
g_signal_connect(G_OBJECT(widget), "unrealize", G_CALLBACK(glwidget_context_destroyed), 0);
|
||||
|
||||
return widget;
|
||||
}
|
||||
|
||||
void glwidget_destroy_context (GtkWidget *widget)
|
||||
{
|
||||
}
|
||||
|
||||
void glwidget_create_context (GtkWidget *widget)
|
||||
{
|
||||
}
|
||||
|
||||
void glwidget_swap_buffers (GtkWidget *widget)
|
||||
{
|
||||
GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
|
||||
gdk_gl_drawable_swap_buffers (gldrawable);
|
||||
}
|
||||
|
||||
gboolean glwidget_make_current (GtkWidget *widget)
|
||||
{
|
||||
GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
|
||||
GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
|
||||
return gdk_gl_drawable_gl_begin (gldrawable, glcontext);
|
||||
}
|
||||
|
19
tools/urt/libs/gtkutil/glwidget.h
Normal file
19
tools/urt/libs/gtkutil/glwidget.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
|
||||
#if !defined(INCLUDED_GTKUTIL_GLWIDGET_H)
|
||||
#define INCLUDED_GTKUTIL_GLWIDGET_H
|
||||
|
||||
typedef struct _GtkWidget GtkWidget;
|
||||
typedef int gint;
|
||||
typedef gint gboolean;
|
||||
|
||||
GtkWidget* glwidget_new(gboolean zbuffer);
|
||||
void glwidget_swap_buffers(GtkWidget* widget);
|
||||
gboolean glwidget_make_current(GtkWidget* widget);
|
||||
void glwidget_destroy_context(GtkWidget* widget);
|
||||
void glwidget_create_context(GtkWidget* widget);
|
||||
|
||||
extern void (*GLWidget_sharedContextCreated)();
|
||||
extern void (*GLWidget_sharedContextDestroyed)();
|
||||
|
||||
|
||||
#endif
|
255
tools/urt/libs/gtkutil/gtkutil.vcproj
Normal file
255
tools/urt/libs/gtkutil/gtkutil.vcproj
Normal file
|
@ -0,0 +1,255 @@
|
|||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="7.10"
|
||||
Name="gtkutil"
|
||||
ProjectGUID="{68E2C6B6-96CA-4BBD-A485-FEE6F2E65407}"
|
||||
Keyword="Win32Proj">
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"/>
|
||||
</Platforms>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="Debug"
|
||||
IntermediateDirectory="Debug"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="../../include;../;"../../../STLPort-4.6/stlport";"../../../gtk2-2.4/lib/glib-2.0/include";"../../../gtk2-2.4/include/glib-2.0";"../../../gtk2-2.4/lib/gtk-2.0/include";"../../../gtk2-2.4/include/gtk-2.0";"../../../gtk2-2.4/include/gtk-2.0/gdk";"../../../gtk2-2.4/include/pango-1.0";"../../../gtk2-2.4/include/atk-1.0";"../../../gtk2-2.4/lib/gtkglext-1.0/include";"../../../gtk2-2.4/include/gtkglext-1.0""
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
|
||||
StringPooling="TRUE"
|
||||
MinimalRebuild="TRUE"
|
||||
ExceptionHandling="FALSE"
|
||||
BasicRuntimeChecks="0"
|
||||
RuntimeLibrary="3"
|
||||
UsePrecompiledHeader="0"
|
||||
BrowseInformation="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="TRUE"
|
||||
DebugInformationFormat="4"
|
||||
DisableSpecificWarnings="4610;4510;4512;4505;4100;4127"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
OutputFile="$(OutDir)/gtkutil.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"
|
||||
IntermediateDirectory="Release"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
GlobalOptimizations="TRUE"
|
||||
InlineFunctionExpansion="2"
|
||||
EnableIntrinsicFunctions="TRUE"
|
||||
OptimizeForWindowsApplication="TRUE"
|
||||
AdditionalIncludeDirectories="../../include;../;"../../../STLPort-4.6/stlport";"../../../gtk2-2.4/lib/glib-2.0/include";"../../../gtk2-2.4/include/glib-2.0";"../../../gtk2-2.4/lib/gtk-2.0/include";"../../../gtk2-2.4/include/gtk-2.0";"../../../gtk2-2.4/include/gtk-2.0/gdk";"../../../gtk2-2.4/include/pango-1.0";"../../../gtk2-2.4/include/atk-1.0";"../../../gtk2-2.4/lib/gtkglext-1.0/include";"../../../gtk2-2.4/include/gtkglext-1.0""
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
|
||||
StringPooling="TRUE"
|
||||
ExceptionHandling="FALSE"
|
||||
RuntimeLibrary="2"
|
||||
BufferSecurityCheck="FALSE"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="4"
|
||||
Detect64BitPortabilityProblems="TRUE"
|
||||
DebugInformationFormat="3"
|
||||
DisableSpecificWarnings="4610;4510;4512;4505;4100;4127"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
OutputFile="$(OutDir)/gtkutil.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>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="src"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath=".\accelerator.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\accelerator.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\button.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\button.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\clipboard.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\clipboard.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\closure.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\closure.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\container.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\container.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\cursor.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\cursor.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\dialog.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\dialog.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\entry.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\entry.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\filechooser.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\filechooser.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\frame.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\frame.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\glfont.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\glfont.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\glwidget.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\glwidget.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\idledraw.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\idledraw.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\image.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\image.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\menu.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\menu.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\messagebox.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\messagebox.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\nonmodal.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\nonmodal.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\paned.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\paned.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\pointer.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\pointer.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\toolbar.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\toolbar.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\widget.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\widget.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\window.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\window.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\xorrectangle.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\xorrectangle.h">
|
||||
</File>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
3
tools/urt/libs/gtkutil/idledraw.cpp
Normal file
3
tools/urt/libs/gtkutil/idledraw.cpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
#include "idledraw.h"
|
||||
|
49
tools/urt/libs/gtkutil/idledraw.h
Normal file
49
tools/urt/libs/gtkutil/idledraw.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
|
||||
#if !defined(INCLUDED_GTKUTIL_IDLEDRAW_H)
|
||||
#define INCLUDED_GTKUTIL_IDLEDRAW_H
|
||||
|
||||
#include <glib/gmain.h>
|
||||
|
||||
#include "generic/callback.h"
|
||||
|
||||
class IdleDraw
|
||||
{
|
||||
Callback m_draw;
|
||||
unsigned int m_handler;
|
||||
static gboolean draw(gpointer data)
|
||||
{
|
||||
reinterpret_cast<IdleDraw*>(data)->m_draw();
|
||||
reinterpret_cast<IdleDraw*>(data)->m_handler = 0;
|
||||
return FALSE;
|
||||
}
|
||||
public:
|
||||
IdleDraw(const Callback& draw) : m_draw(draw), m_handler(0)
|
||||
{
|
||||
}
|
||||
~IdleDraw()
|
||||
{
|
||||
if(m_handler != 0)
|
||||
{
|
||||
g_source_remove(m_handler);
|
||||
}
|
||||
}
|
||||
void queueDraw()
|
||||
{
|
||||
if(m_handler == 0)
|
||||
{
|
||||
m_handler = g_idle_add(&draw, this);
|
||||
}
|
||||
}
|
||||
typedef MemberCaller<IdleDraw, &IdleDraw::queueDraw> QueueDrawCaller;
|
||||
|
||||
void flush()
|
||||
{
|
||||
if(m_handler != 0)
|
||||
{
|
||||
draw(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
76
tools/urt/libs/gtkutil/image.cpp
Normal file
76
tools/urt/libs/gtkutil/image.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
|
||||
#include "image.h"
|
||||
|
||||
#include <gtk/gtkimage.h>
|
||||
#include <gtk/gtkstock.h>
|
||||
|
||||
#include "string/string.h"
|
||||
#include "stream/stringstream.h"
|
||||
#include "stream/textstream.h"
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
CopiedString g_bitmapsPath;
|
||||
}
|
||||
|
||||
void BitmapsPath_set(const char* path)
|
||||
{
|
||||
g_bitmapsPath = path;
|
||||
}
|
||||
|
||||
GdkPixbuf* pixbuf_new_from_file_with_mask(const char* filename)
|
||||
{
|
||||
GdkPixbuf* rgb = gdk_pixbuf_new_from_file(filename, 0);
|
||||
if(rgb == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
GdkPixbuf* rgba = gdk_pixbuf_add_alpha(rgb, TRUE, 255, 0, 255);
|
||||
gdk_pixbuf_unref(rgb);
|
||||
return rgba;
|
||||
}
|
||||
}
|
||||
|
||||
GtkImage* image_new_from_file_with_mask(const char* filename)
|
||||
{
|
||||
GdkPixbuf* rgba = pixbuf_new_from_file_with_mask(filename);
|
||||
if(rgba == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
GtkImage* image = GTK_IMAGE(gtk_image_new_from_pixbuf(rgba));
|
||||
gdk_pixbuf_unref(rgba);
|
||||
return image;
|
||||
}
|
||||
}
|
||||
|
||||
GtkImage* image_new_missing()
|
||||
{
|
||||
return GTK_IMAGE(gtk_image_new_from_stock(GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_SMALL_TOOLBAR));
|
||||
}
|
||||
|
||||
GtkImage* new_image(const char* filename)
|
||||
{
|
||||
{
|
||||
GtkImage* image = image_new_from_file_with_mask(filename);
|
||||
if(image != 0)
|
||||
{
|
||||
return image;
|
||||
}
|
||||
}
|
||||
|
||||
return image_new_missing();
|
||||
}
|
||||
|
||||
GtkImage* new_local_image(const char* filename)
|
||||
{
|
||||
StringOutputStream fullPath(256);
|
||||
fullPath << g_bitmapsPath.c_str() << filename;
|
||||
return new_image(fullPath.c_str());
|
||||
}
|
||||
|
16
tools/urt/libs/gtkutil/image.h
Normal file
16
tools/urt/libs/gtkutil/image.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
|
||||
#if !defined(INCLUDED_GTKUTIL_IMAGE_H)
|
||||
#define INCLUDED_GTKUTIL_IMAGE_H
|
||||
|
||||
void BitmapsPath_set(const char* path);
|
||||
|
||||
typedef struct _GtkImage GtkImage;
|
||||
typedef struct _GdkPixbuf GdkPixbuf;
|
||||
|
||||
GdkPixbuf* pixbuf_new_from_file_with_mask(const char* filename);
|
||||
GtkImage* image_new_from_file_with_mask(const char* filename);
|
||||
GtkImage* image_new_missing();
|
||||
GtkImage* new_image(const char* filename); // filename is full path to image file
|
||||
GtkImage* new_local_image(const char* filename); // filename is relative to local bitmaps path
|
||||
|
||||
#endif
|
293
tools/urt/libs/gtkutil/menu.cpp
Normal file
293
tools/urt/libs/gtkutil/menu.cpp
Normal file
|
@ -0,0 +1,293 @@
|
|||
|
||||
#include "menu.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <gtk/gtkmenu.h>
|
||||
#include <gtk/gtkmenubar.h>
|
||||
#include <gtk/gtkradiomenuitem.h>
|
||||
#include <gtk/gtktearoffmenuitem.h>
|
||||
#include <gtk/gtkaccellabel.h>
|
||||
|
||||
#include "generic/callback.h"
|
||||
|
||||
#include "accelerator.h"
|
||||
#include "closure.h"
|
||||
#include "container.h"
|
||||
#include "pointer.h"
|
||||
|
||||
void menu_add_item(GtkMenu* menu, GtkMenuItem* item)
|
||||
{
|
||||
gtk_container_add(GTK_CONTAINER(menu), GTK_WIDGET(item));
|
||||
}
|
||||
|
||||
GtkMenuItem* menu_separator(GtkMenu* menu)
|
||||
{
|
||||
GtkMenuItem* menu_item = GTK_MENU_ITEM(gtk_menu_item_new());
|
||||
container_add_widget(GTK_CONTAINER(menu), GTK_WIDGET(menu_item));
|
||||
gtk_widget_set_sensitive(GTK_WIDGET(menu_item), FALSE);
|
||||
gtk_widget_show(GTK_WIDGET(menu_item));
|
||||
return menu_item;
|
||||
}
|
||||
|
||||
GtkTearoffMenuItem* menu_tearoff(GtkMenu* menu)
|
||||
{
|
||||
GtkTearoffMenuItem* menu_item = GTK_TEAROFF_MENU_ITEM(gtk_tearoff_menu_item_new());
|
||||
container_add_widget(GTK_CONTAINER(menu), GTK_WIDGET(menu_item));
|
||||
// gtk_widget_set_sensitive(GTK_WIDGET(menu_item), FALSE); -- controls whether menu is detachable
|
||||
gtk_widget_show(GTK_WIDGET(menu_item));
|
||||
return menu_item;
|
||||
}
|
||||
|
||||
GtkMenuItem* new_sub_menu_item_with_mnemonic(const char* mnemonic)
|
||||
{
|
||||
GtkMenuItem* item = GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(mnemonic));
|
||||
gtk_widget_show(GTK_WIDGET(item));
|
||||
|
||||
GtkWidget* sub_menu = gtk_menu_new();
|
||||
gtk_menu_item_set_submenu(item, sub_menu);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
GtkMenu* create_sub_menu_with_mnemonic(GtkMenuShell* parent, const char* mnemonic)
|
||||
{
|
||||
GtkMenuItem* item = new_sub_menu_item_with_mnemonic(mnemonic);
|
||||
container_add_widget(GTK_CONTAINER(parent), GTK_WIDGET(item));
|
||||
return GTK_MENU(gtk_menu_item_get_submenu(item));
|
||||
}
|
||||
|
||||
GtkMenu* create_sub_menu_with_mnemonic(GtkMenuBar* bar, const char* mnemonic)
|
||||
{
|
||||
return create_sub_menu_with_mnemonic(GTK_MENU_SHELL(bar), mnemonic);
|
||||
}
|
||||
|
||||
GtkMenu* create_sub_menu_with_mnemonic(GtkMenu* parent, const char* mnemonic)
|
||||
{
|
||||
return create_sub_menu_with_mnemonic(GTK_MENU_SHELL(parent), mnemonic);
|
||||
}
|
||||
|
||||
void activate_closure_callback(GtkWidget* widget, gpointer data)
|
||||
{
|
||||
(*reinterpret_cast<Callback*>(data))();
|
||||
}
|
||||
|
||||
guint menu_item_connect_callback(GtkMenuItem* item, const Callback& callback)
|
||||
{
|
||||
#if 1
|
||||
return g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(callback.getThunk()), callback.getEnvironment());
|
||||
#else
|
||||
return g_signal_connect_closure(G_OBJECT(item), "activate", create_cclosure(G_CALLBACK(activate_closure_callback), callback), FALSE);
|
||||
#endif
|
||||
}
|
||||
|
||||
guint check_menu_item_connect_callback(GtkCheckMenuItem* item, const Callback& callback)
|
||||
{
|
||||
#if 1
|
||||
guint handler = g_signal_connect_swapped(G_OBJECT(item), "toggled", G_CALLBACK(callback.getThunk()), callback.getEnvironment());
|
||||
#else
|
||||
guint handler = g_signal_connect_closure(G_OBJECT(item), "toggled", create_cclosure(G_CALLBACK(activate_closure_callback), callback), TRUE);
|
||||
#endif
|
||||
g_object_set_data(G_OBJECT(item), "handler", gint_to_pointer(handler));
|
||||
return handler;
|
||||
}
|
||||
|
||||
GtkMenuItem* new_menu_item_with_mnemonic(const char *mnemonic, const Callback& callback)
|
||||
{
|
||||
GtkMenuItem* item = GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(mnemonic));
|
||||
gtk_widget_show(GTK_WIDGET(item));
|
||||
menu_item_connect_callback(item, callback);
|
||||
return item;
|
||||
}
|
||||
|
||||
GtkMenuItem* create_menu_item_with_mnemonic(GtkMenu* menu, const char *mnemonic, const Callback& callback)
|
||||
{
|
||||
GtkMenuItem* item = new_menu_item_with_mnemonic(mnemonic, callback);
|
||||
container_add_widget(GTK_CONTAINER(menu), GTK_WIDGET(item));
|
||||
return item;
|
||||
}
|
||||
|
||||
GtkCheckMenuItem* new_check_menu_item_with_mnemonic(const char* mnemonic, const Callback& callback)
|
||||
{
|
||||
GtkCheckMenuItem* item = GTK_CHECK_MENU_ITEM(gtk_check_menu_item_new_with_mnemonic(mnemonic));
|
||||
gtk_widget_show(GTK_WIDGET(item));
|
||||
check_menu_item_connect_callback(item, callback);
|
||||
return item;
|
||||
}
|
||||
|
||||
GtkCheckMenuItem* create_check_menu_item_with_mnemonic(GtkMenu* menu, const char* mnemonic, const Callback& callback)
|
||||
{
|
||||
GtkCheckMenuItem* item = new_check_menu_item_with_mnemonic(mnemonic, callback);
|
||||
container_add_widget(GTK_CONTAINER(menu), GTK_WIDGET(item));
|
||||
return item;
|
||||
}
|
||||
|
||||
GtkRadioMenuItem* new_radio_menu_item_with_mnemonic(GSList** group, const char* mnemonic, const Callback& callback)
|
||||
{
|
||||
GtkRadioMenuItem* item = GTK_RADIO_MENU_ITEM(gtk_radio_menu_item_new_with_mnemonic(*group, mnemonic));
|
||||
if(*group == 0)
|
||||
{
|
||||
gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(item), TRUE);
|
||||
}
|
||||
*group = gtk_radio_menu_item_group(item);
|
||||
gtk_widget_show(GTK_WIDGET(item));
|
||||
check_menu_item_connect_callback(GTK_CHECK_MENU_ITEM(item), callback);
|
||||
return item;
|
||||
}
|
||||
|
||||
GtkRadioMenuItem* create_radio_menu_item_with_mnemonic(GtkMenu* menu, GSList** group, const char* mnemonic, const Callback& callback)
|
||||
{
|
||||
GtkRadioMenuItem* item = new_radio_menu_item_with_mnemonic(group, mnemonic, callback);
|
||||
container_add_widget(GTK_CONTAINER(menu), GTK_WIDGET(item));
|
||||
return item;
|
||||
}
|
||||
|
||||
void check_menu_item_set_active_no_signal(GtkCheckMenuItem* item, gboolean active)
|
||||
{
|
||||
guint handler_id = gpointer_to_int(g_object_get_data(G_OBJECT(item), "handler"));
|
||||
g_signal_handler_block(G_OBJECT(item), handler_id);
|
||||
gtk_check_menu_item_set_active(item, active);
|
||||
g_signal_handler_unblock(G_OBJECT(item), handler_id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void radio_menu_item_set_active_no_signal(GtkRadioMenuItem* item, gboolean active)
|
||||
{
|
||||
{
|
||||
for(GSList* l = gtk_radio_menu_item_get_group(item); l != 0; l = g_slist_next(l))
|
||||
{
|
||||
g_signal_handler_block(G_OBJECT(l->data), gpointer_to_int(g_object_get_data(G_OBJECT(l->data), "handler")));
|
||||
}
|
||||
}
|
||||
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), active);
|
||||
{
|
||||
for(GSList* l = gtk_radio_menu_item_get_group(item); l != 0; l = g_slist_next(l))
|
||||
{
|
||||
g_signal_handler_unblock(G_OBJECT(l->data), gpointer_to_int(g_object_get_data(G_OBJECT(l->data), "handler")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void menu_item_set_accelerator(GtkMenuItem* item, GClosure* closure)
|
||||
{
|
||||
GtkAccelLabel* accel_label = GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(item)));
|
||||
gtk_accel_label_set_accel_closure(accel_label, closure);
|
||||
}
|
||||
|
||||
void accelerator_name(const Accelerator& accelerator, GString* gstring)
|
||||
{
|
||||
gboolean had_mod = FALSE;
|
||||
if (accelerator.modifiers & GDK_SHIFT_MASK)
|
||||
{
|
||||
g_string_append (gstring, "Shift");
|
||||
had_mod = TRUE;
|
||||
}
|
||||
if (accelerator.modifiers & GDK_CONTROL_MASK)
|
||||
{
|
||||
if (had_mod)
|
||||
g_string_append (gstring, "+");
|
||||
g_string_append (gstring, "Ctrl");
|
||||
had_mod = TRUE;
|
||||
}
|
||||
if (accelerator.modifiers & GDK_MOD1_MASK)
|
||||
{
|
||||
if (had_mod)
|
||||
g_string_append (gstring, "+");
|
||||
g_string_append (gstring, "Alt");
|
||||
had_mod = TRUE;
|
||||
}
|
||||
|
||||
if (had_mod)
|
||||
g_string_append (gstring, "+");
|
||||
if (accelerator.key < 0x80 || (accelerator.key > 0x80 && accelerator.key <= 0xff))
|
||||
{
|
||||
switch (accelerator.key)
|
||||
{
|
||||
case ' ':
|
||||
g_string_append (gstring, "Space");
|
||||
break;
|
||||
case '\\':
|
||||
g_string_append (gstring, "Backslash");
|
||||
break;
|
||||
default:
|
||||
g_string_append_c (gstring, gchar(toupper(accelerator.key)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gchar *tmp;
|
||||
|
||||
tmp = gtk_accelerator_name (accelerator.key, (GdkModifierType)0);
|
||||
if (tmp[0] != 0 && tmp[1] == 0)
|
||||
tmp[0] = gchar(toupper(tmp[0]));
|
||||
g_string_append (gstring, tmp);
|
||||
g_free (tmp);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_item_set_accelerator(GtkMenuItem* item, Accelerator accelerator)
|
||||
{
|
||||
GtkAccelLabel* accel_label = GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(item)));
|
||||
|
||||
g_free (accel_label->accel_string);
|
||||
accel_label->accel_string = 0;
|
||||
|
||||
GString* gstring = g_string_new (accel_label->accel_string);
|
||||
g_string_append (gstring, " ");
|
||||
|
||||
accelerator_name(accelerator, gstring);
|
||||
|
||||
g_free (accel_label->accel_string);
|
||||
accel_label->accel_string = gstring->str;
|
||||
g_string_free (gstring, FALSE);
|
||||
|
||||
if (!accel_label->accel_string)
|
||||
accel_label->accel_string = g_strdup ("");
|
||||
|
||||
gtk_widget_queue_resize (GTK_WIDGET (accel_label));
|
||||
}
|
||||
|
||||
void menu_item_add_accelerator(GtkMenuItem* item, Accelerator accelerator)
|
||||
{
|
||||
if(accelerator.key != 0)
|
||||
{
|
||||
GClosure* closure = global_accel_group_find(accelerator);
|
||||
if(closure != 0)
|
||||
{
|
||||
menu_item_set_accelerator(item, closure);
|
||||
}
|
||||
else
|
||||
{
|
||||
menu_item_set_accelerator(item, accelerator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GtkMenuItem* create_menu_item_with_mnemonic(GtkMenu* menu, const char* mnemonic, const Command& command)
|
||||
{
|
||||
command_connect_accelerator(command.m_accelerator, command.m_callback);
|
||||
GtkMenuItem* item = create_menu_item_with_mnemonic(menu, mnemonic, command.m_callback);
|
||||
menu_item_add_accelerator(item, command.m_accelerator);
|
||||
return item;
|
||||
}
|
||||
|
||||
void check_menu_item_set_active_callback(GtkCheckMenuItem& item, bool enabled)
|
||||
{
|
||||
check_menu_item_set_active_no_signal(&item, enabled);
|
||||
}
|
||||
typedef ReferenceCaller1<GtkCheckMenuItem, bool, check_menu_item_set_active_callback> CheckMenuItemSetActiveCaller;
|
||||
|
||||
GtkCheckMenuItem* create_check_menu_item_with_mnemonic(GtkMenu* menu, const char* mnemonic, const Toggle& toggle)
|
||||
{
|
||||
command_connect_accelerator(toggle.m_command.m_accelerator, toggle.m_command.m_callback);
|
||||
GtkCheckMenuItem* item = create_check_menu_item_with_mnemonic(menu, mnemonic, toggle.m_command.m_callback);
|
||||
menu_item_add_accelerator(GTK_MENU_ITEM(item), toggle.m_command.m_accelerator);
|
||||
toggle.m_exportCallback(CheckMenuItemSetActiveCaller(*item));
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
37
tools/urt/libs/gtkutil/menu.h
Normal file
37
tools/urt/libs/gtkutil/menu.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
|
||||
#if !defined(INCLUDED_GTKUTIL_MENU_H)
|
||||
#define INCLUDED_GTKUTIL_MENU_H
|
||||
|
||||
class Callback;
|
||||
typedef int gint;
|
||||
typedef gint gboolean;
|
||||
typedef struct _GSList GSList;
|
||||
typedef struct _GtkMenu GtkMenu;
|
||||
typedef struct _GtkMenuBar GtkMenuBar;
|
||||
typedef struct _GtkMenuItem GtkMenuItem;
|
||||
typedef struct _GtkCheckMenuItem GtkCheckMenuItem;
|
||||
typedef struct _GtkRadioMenuItem GtkRadioMenuItem;
|
||||
typedef struct _GtkTearoffMenuItem GtkTearoffMenuItem;
|
||||
|
||||
void menu_add_item(GtkMenu* menu, GtkMenuItem* item);
|
||||
GtkMenuItem* menu_separator(GtkMenu* menu);
|
||||
GtkTearoffMenuItem* menu_tearoff(GtkMenu* menu);
|
||||
GtkMenuItem* new_sub_menu_item_with_mnemonic(const char* mnemonic);
|
||||
GtkMenu* create_sub_menu_with_mnemonic(GtkMenuBar* bar, const char* mnemonic);
|
||||
GtkMenu* create_sub_menu_with_mnemonic(GtkMenu* parent, const char* mnemonic);
|
||||
GtkMenuItem* create_menu_item_with_mnemonic(GtkMenu* menu, const char* mnemonic, const Callback& callback);
|
||||
GtkCheckMenuItem* create_check_menu_item_with_mnemonic(GtkMenu* menu, const char* mnemonic, const Callback& callback);
|
||||
GtkRadioMenuItem* create_radio_menu_item_with_mnemonic(GtkMenu* menu, GSList** group, const char* mnemonic, const Callback& callback);
|
||||
|
||||
class Command;
|
||||
GtkMenuItem* create_menu_item_with_mnemonic(GtkMenu* menu, const char* mnemonic, const Command& command);
|
||||
class Toggle;
|
||||
GtkCheckMenuItem* create_check_menu_item_with_mnemonic(GtkMenu* menu, const char* mnemonic, const Toggle& toggle);
|
||||
|
||||
|
||||
typedef struct _GtkCheckMenuItem GtkCheckMenuItem;
|
||||
void check_menu_item_set_active_no_signal(GtkCheckMenuItem* item, gboolean active);
|
||||
typedef struct _GtkRadioMenuItem GtkRadioMenuItem;
|
||||
void radio_menu_item_set_active_no_signal(GtkRadioMenuItem* item, gboolean active);
|
||||
|
||||
#endif
|
193
tools/urt/libs/gtkutil/messagebox.cpp
Normal file
193
tools/urt/libs/gtkutil/messagebox.cpp
Normal file
|
@ -0,0 +1,193 @@
|
|||
|
||||
#include "messagebox.h"
|
||||
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#include <gtk/gtkmain.h>
|
||||
#include <gtk/gtkwindow.h>
|
||||
#include <gtk/gtkhbox.h>
|
||||
#include <gtk/gtkvbox.h>
|
||||
#include <gtk/gtklabel.h>
|
||||
#include <gtk/gtkalignment.h>
|
||||
#include <gtk/gtkbutton.h>
|
||||
#include <gtk/gtkimage.h>
|
||||
#include <gtk/gtkstock.h>
|
||||
|
||||
#include "dialog.h"
|
||||
#include "widget.h"
|
||||
|
||||
GtkWidget* create_padding(int width, int height)
|
||||
{
|
||||
GtkWidget* widget = gtk_alignment_new(0.0, 0.0, 0.0, 0.0);
|
||||
gtk_widget_show(widget);
|
||||
gtk_widget_set_size_request(widget, width, height);
|
||||
return widget;
|
||||
}
|
||||
|
||||
const char* messagebox_stock_icon(EMessageBoxIcon type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
default:
|
||||
case eMB_ICONDEFAULT:
|
||||
return GTK_STOCK_DIALOG_INFO;
|
||||
case eMB_ICONERROR:
|
||||
return GTK_STOCK_DIALOG_ERROR;
|
||||
case eMB_ICONWARNING:
|
||||
return GTK_STOCK_DIALOG_WARNING;
|
||||
case eMB_ICONQUESTION:
|
||||
return GTK_STOCK_DIALOG_QUESTION;
|
||||
case eMB_ICONASTERISK:
|
||||
return GTK_STOCK_DIALOG_INFO;
|
||||
}
|
||||
}
|
||||
|
||||
EMessageBoxReturn gtk_MessageBox (GtkWidget *parent, const char* text, const char* title, EMessageBoxType type, EMessageBoxIcon icon)
|
||||
{
|
||||
ModalDialog dialog;
|
||||
ModalDialogButton ok_button(dialog, eIDOK);
|
||||
ModalDialogButton cancel_button(dialog, eIDCANCEL);
|
||||
ModalDialogButton yes_button(dialog, eIDYES);
|
||||
ModalDialogButton no_button(dialog, eIDNO);
|
||||
|
||||
GtkWindow* parentWindow = parent != 0 ? GTK_WINDOW(parent) : 0;
|
||||
|
||||
GtkWindow* window = create_fixedsize_modal_dialog_window(parentWindow, title, dialog, 400, 100);
|
||||
|
||||
if(parentWindow != 0)
|
||||
{
|
||||
//g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(floating_window_delete_present), parent);
|
||||
gtk_window_deiconify(parentWindow);
|
||||
}
|
||||
|
||||
GtkAccelGroup* accel = gtk_accel_group_new();
|
||||
gtk_window_add_accel_group(window, accel);
|
||||
|
||||
GtkVBox* vbox = create_dialog_vbox(8, 8);
|
||||
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox));
|
||||
|
||||
|
||||
GtkHBox* hboxDummy = create_dialog_hbox(0, 0);
|
||||
gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hboxDummy), FALSE, FALSE, 0);
|
||||
|
||||
gtk_box_pack_start(GTK_BOX(hboxDummy), create_padding(0, 50), FALSE, FALSE, 0); // HACK to force minimum height
|
||||
|
||||
GtkHBox* iconBox = create_dialog_hbox(16, 0);
|
||||
gtk_box_pack_start(GTK_BOX(hboxDummy), GTK_WIDGET(iconBox), FALSE, FALSE, 0);
|
||||
|
||||
GtkImage* image = GTK_IMAGE(gtk_image_new_from_stock(messagebox_stock_icon(icon), GTK_ICON_SIZE_DIALOG));
|
||||
gtk_widget_show(GTK_WIDGET(image));
|
||||
gtk_box_pack_start(GTK_BOX(iconBox), GTK_WIDGET(image), FALSE, FALSE, 0);
|
||||
|
||||
GtkLabel* label = GTK_LABEL(gtk_label_new(text));
|
||||
gtk_widget_show(GTK_WIDGET(label));
|
||||
gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
|
||||
gtk_label_set_justify(label, GTK_JUSTIFY_LEFT);
|
||||
gtk_label_set_line_wrap(label, TRUE);
|
||||
gtk_box_pack_start(GTK_BOX(iconBox), GTK_WIDGET(label), TRUE, TRUE, 0);
|
||||
|
||||
|
||||
GtkVBox* vboxDummy = create_dialog_vbox(0, 0);
|
||||
gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(vboxDummy), FALSE, FALSE, 0);
|
||||
|
||||
GtkAlignment* alignment = GTK_ALIGNMENT(gtk_alignment_new(0.5, 0.0, 0.0, 0.0));
|
||||
gtk_widget_show(GTK_WIDGET(alignment));
|
||||
gtk_box_pack_start(GTK_BOX(vboxDummy), GTK_WIDGET(alignment), FALSE, FALSE, 0);
|
||||
|
||||
GtkHBox* hbox = create_dialog_hbox(8, 0);
|
||||
gtk_container_add(GTK_CONTAINER(alignment), GTK_WIDGET(hbox));
|
||||
|
||||
gtk_box_pack_start(GTK_BOX(vboxDummy), create_padding(400, 0), FALSE, FALSE, 0); // HACK to force minimum width
|
||||
|
||||
|
||||
if (type == eMB_OK)
|
||||
{
|
||||
GtkButton* button = create_modal_dialog_button("OK", ok_button);
|
||||
gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), TRUE, FALSE, 0);
|
||||
gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0);
|
||||
gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0);
|
||||
widget_make_default(GTK_WIDGET(button));
|
||||
gtk_widget_show(GTK_WIDGET(button));
|
||||
|
||||
dialog.ret = eIDOK;
|
||||
}
|
||||
else if (type == eMB_OKCANCEL)
|
||||
{
|
||||
{
|
||||
GtkButton* button = create_modal_dialog_button("OK", ok_button);
|
||||
gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), TRUE, FALSE, 0);
|
||||
gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0);
|
||||
widget_make_default(GTK_WIDGET(button));
|
||||
gtk_widget_show(GTK_WIDGET(button));
|
||||
}
|
||||
|
||||
{
|
||||
GtkButton* button = create_modal_dialog_button("OK", cancel_button);
|
||||
gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), TRUE, FALSE, 0);
|
||||
gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0);
|
||||
gtk_widget_show(GTK_WIDGET(button));
|
||||
}
|
||||
|
||||
dialog.ret = eIDCANCEL;
|
||||
}
|
||||
else if (type == eMB_YESNOCANCEL)
|
||||
{
|
||||
{
|
||||
GtkButton* button = create_modal_dialog_button("Yes", yes_button);
|
||||
gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), TRUE, FALSE, 0);
|
||||
widget_make_default(GTK_WIDGET(button));
|
||||
gtk_widget_show(GTK_WIDGET(button));
|
||||
}
|
||||
|
||||
{
|
||||
GtkButton* button = create_modal_dialog_button("No", no_button);
|
||||
gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), TRUE, FALSE, 0);
|
||||
gtk_widget_show(GTK_WIDGET(button));
|
||||
}
|
||||
{
|
||||
GtkButton* button = create_modal_dialog_button("Cancel", cancel_button);
|
||||
gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), TRUE, FALSE, 0);
|
||||
gtk_widget_show(GTK_WIDGET(button));
|
||||
}
|
||||
|
||||
dialog.ret = eIDCANCEL;
|
||||
}
|
||||
else if (type == eMB_NOYES)
|
||||
{
|
||||
{
|
||||
GtkButton* button = create_modal_dialog_button("No", no_button);
|
||||
gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), TRUE, FALSE, 0);
|
||||
widget_make_default(GTK_WIDGET(button));
|
||||
gtk_widget_show(GTK_WIDGET(button));
|
||||
}
|
||||
{
|
||||
GtkButton* button = create_modal_dialog_button("Yes", yes_button);
|
||||
gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), TRUE, FALSE, 0);
|
||||
gtk_widget_show(GTK_WIDGET(button));
|
||||
}
|
||||
|
||||
dialog.ret = eIDNO;
|
||||
}
|
||||
else /* if (type == eMB_YESNO) */
|
||||
{
|
||||
{
|
||||
GtkButton* button = create_modal_dialog_button("Yes", yes_button);
|
||||
gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), TRUE, FALSE, 0);
|
||||
widget_make_default(GTK_WIDGET(button));
|
||||
gtk_widget_show(GTK_WIDGET(button));
|
||||
}
|
||||
|
||||
{
|
||||
GtkButton* button = create_modal_dialog_button("No", no_button);
|
||||
gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), TRUE, FALSE, 0);
|
||||
gtk_widget_show(GTK_WIDGET(button));
|
||||
}
|
||||
dialog.ret = eIDNO;
|
||||
}
|
||||
|
||||
modal_dialog_show(window, dialog);
|
||||
|
||||
gtk_widget_destroy(GTK_WIDGET(window));
|
||||
|
||||
return dialog.ret;
|
||||
}
|
||||
|
11
tools/urt/libs/gtkutil/messagebox.h
Normal file
11
tools/urt/libs/gtkutil/messagebox.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
#if !defined(INCLUDED_GTKUTIL_MESSAGEBOX_H)
|
||||
#define INCLUDED_GTKUTIL_MESSAGEBOX_H
|
||||
|
||||
#include "qerplugin.h"
|
||||
|
||||
typedef struct _GtkWidget GtkWidget;
|
||||
/// \brief Shows a modal message-box.
|
||||
EMessageBoxReturn gtk_MessageBox(GtkWidget *parent, const char* text, const char* title = "GtkRadiant", EMessageBoxType type = eMB_OK, EMessageBoxIcon icon = eMB_ICONDEFAULT);
|
||||
|
||||
#endif
|
3
tools/urt/libs/gtkutil/nonmodal.cpp
Normal file
3
tools/urt/libs/gtkutil/nonmodal.cpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
#include "nonmodal.h"
|
||||
|
166
tools/urt/libs/gtkutil/nonmodal.h
Normal file
166
tools/urt/libs/gtkutil/nonmodal.h
Normal file
|
@ -0,0 +1,166 @@
|
|||
|
||||
#if !defined(INCLUDED_GTKUTIL_NONMODAL_H)
|
||||
#define INCLUDED_GTKUTIL_NONMODAL_H
|
||||
|
||||
#include <gtk/gtkwindow.h>
|
||||
#include <gtk/gtkspinbutton.h>
|
||||
#include <gtk/gtkradiobutton.h>
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
|
||||
#include "generic/callback.h"
|
||||
|
||||
#include "pointer.h"
|
||||
#include "button.h"
|
||||
|
||||
typedef struct _GtkEntry GtkEntry;
|
||||
|
||||
|
||||
inline gboolean escape_clear_focus_widget(GtkWidget* widget, GdkEventKey* event, gpointer data)
|
||||
{
|
||||
if(event->keyval == GDK_Escape)
|
||||
{
|
||||
gtk_window_set_focus(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(widget))), NULL);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
inline void widget_connect_escape_clear_focus_widget(GtkWidget* widget)
|
||||
{
|
||||
g_signal_connect(G_OBJECT(widget), "key_press_event", G_CALLBACK(escape_clear_focus_widget), 0);
|
||||
}
|
||||
|
||||
|
||||
class NonModalEntry
|
||||
{
|
||||
bool m_editing;
|
||||
Callback m_apply;
|
||||
Callback m_cancel;
|
||||
|
||||
static gboolean focus_in(GtkEntry* entry, GdkEventFocus *event, NonModalEntry* self)
|
||||
{
|
||||
self->m_editing = false;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean focus_out(GtkEntry* entry, GdkEventFocus *event, NonModalEntry* self)
|
||||
{
|
||||
if(self->m_editing && GTK_WIDGET_VISIBLE(entry))
|
||||
{
|
||||
self->m_apply();
|
||||
}
|
||||
self->m_editing = false;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean changed(GtkEntry* entry, NonModalEntry* self)
|
||||
{
|
||||
self->m_editing = true;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean enter(GtkEntry* entry, GdkEventKey* event, NonModalEntry* self)
|
||||
{
|
||||
if(event->keyval == GDK_Return)
|
||||
{
|
||||
self->m_apply();
|
||||
self->m_editing = false;
|
||||
gtk_window_set_focus(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(entry))), NULL);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean escape(GtkEntry* entry, GdkEventKey* event, NonModalEntry* self)
|
||||
{
|
||||
if(event->keyval == GDK_Escape)
|
||||
{
|
||||
self->m_cancel();
|
||||
self->m_editing = false;
|
||||
gtk_window_set_focus(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(entry))), NULL);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
public:
|
||||
NonModalEntry(const Callback& apply, const Callback& cancel) : m_editing(false), m_apply(apply), m_cancel(cancel)
|
||||
{
|
||||
}
|
||||
void connect(GtkEntry* entry)
|
||||
{
|
||||
g_signal_connect(G_OBJECT(entry), "focus_in_event", G_CALLBACK(focus_in), this);
|
||||
g_signal_connect(G_OBJECT(entry), "focus_out_event", G_CALLBACK(focus_out), this);
|
||||
g_signal_connect(G_OBJECT(entry), "key_press_event", G_CALLBACK(enter), this);
|
||||
g_signal_connect(G_OBJECT(entry), "key_press_event", G_CALLBACK(escape), this);
|
||||
g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(changed), this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class NonModalSpinner
|
||||
{
|
||||
Callback m_apply;
|
||||
Callback m_cancel;
|
||||
|
||||
static gboolean changed(GtkSpinButton* spin, NonModalSpinner* self)
|
||||
{
|
||||
self->m_apply();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean enter(GtkSpinButton* spin, GdkEventKey* event, NonModalSpinner* self)
|
||||
{
|
||||
if(event->keyval == GDK_Return)
|
||||
{
|
||||
gtk_window_set_focus(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(spin))), NULL);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean escape(GtkSpinButton* spin, GdkEventKey* event, NonModalSpinner* self)
|
||||
{
|
||||
if(event->keyval == GDK_Escape)
|
||||
{
|
||||
self->m_cancel();
|
||||
gtk_window_set_focus(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(spin))), NULL);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
public:
|
||||
NonModalSpinner(const Callback& apply, const Callback& cancel) : m_apply(apply), m_cancel(cancel)
|
||||
{
|
||||
}
|
||||
void connect(GtkSpinButton* spin)
|
||||
{
|
||||
guint handler = g_signal_connect(G_OBJECT(gtk_spin_button_get_adjustment(spin)), "value_changed", G_CALLBACK(changed), this);
|
||||
g_object_set_data(G_OBJECT(spin), "handler", gint_to_pointer(handler));
|
||||
g_signal_connect(G_OBJECT(spin), "key_press_event", G_CALLBACK(enter), this);
|
||||
g_signal_connect(G_OBJECT(spin), "key_press_event", G_CALLBACK(escape), this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class NonModalRadio
|
||||
{
|
||||
Callback m_changed;
|
||||
|
||||
public:
|
||||
NonModalRadio(const Callback& changed) : m_changed(changed)
|
||||
{
|
||||
}
|
||||
void connect(GtkRadioButton* radio)
|
||||
{
|
||||
GSList* group = gtk_radio_button_group(radio);
|
||||
for(; group != 0; group = g_slist_next(group))
|
||||
{
|
||||
toggle_button_connect_callback(GTK_TOGGLE_BUTTON(group->data), m_changed);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
80
tools/urt/libs/gtkutil/paned.cpp
Normal file
80
tools/urt/libs/gtkutil/paned.cpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
|
||||
#include "paned.h"
|
||||
|
||||
#include <gtk/gtkhpaned.h>
|
||||
#include <gtk/gtkvpaned.h>
|
||||
|
||||
#include "frame.h"
|
||||
|
||||
|
||||
class PanedState
|
||||
{
|
||||
public:
|
||||
float position;
|
||||
int size;
|
||||
};
|
||||
|
||||
gboolean hpaned_allocate(GtkWidget* widget, GtkAllocation* allocation, PanedState* paned)
|
||||
{
|
||||
if(paned->size != allocation->width)
|
||||
{
|
||||
paned->size = allocation->width;
|
||||
gtk_paned_set_position (GTK_PANED (widget), static_cast<int>(paned->size * paned->position));
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean vpaned_allocate(GtkWidget* widget, GtkAllocation* allocation, PanedState* paned)
|
||||
{
|
||||
if(paned->size != allocation->height)
|
||||
{
|
||||
paned->size = allocation->height;
|
||||
gtk_paned_set_position (GTK_PANED (widget), static_cast<int>(paned->size * paned->position));
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean paned_position(GtkWidget* widget, gpointer dummy, PanedState* paned)
|
||||
{
|
||||
if(paned->size != -1)
|
||||
paned->position = gtk_paned_get_position (GTK_PANED (widget)) / static_cast<float>(paned->size);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PanedState g_hpaned = { 0.5f, -1, };
|
||||
PanedState g_vpaned1 = { 0.5f, -1, };
|
||||
PanedState g_vpaned2 = { 0.5f, -1, };
|
||||
|
||||
GtkHPaned* create_split_views(GtkWidget* topleft, GtkWidget* topright, GtkWidget* botleft, GtkWidget* botright)
|
||||
{
|
||||
GtkHPaned* hsplit = GTK_HPANED(gtk_hpaned_new());
|
||||
gtk_widget_show(GTK_WIDGET(hsplit));
|
||||
|
||||
g_signal_connect(G_OBJECT(hsplit), "size_allocate", G_CALLBACK(hpaned_allocate), &g_hpaned);
|
||||
g_signal_connect(G_OBJECT(hsplit), "notify::position", G_CALLBACK(paned_position), &g_hpaned);
|
||||
|
||||
{
|
||||
GtkVPaned* vsplit = GTK_VPANED(gtk_vpaned_new());
|
||||
gtk_paned_add1(GTK_PANED(hsplit), GTK_WIDGET(vsplit));
|
||||
gtk_widget_show(GTK_WIDGET(vsplit));
|
||||
|
||||
g_signal_connect(G_OBJECT(vsplit), "size_allocate", G_CALLBACK(vpaned_allocate), &g_vpaned1);
|
||||
g_signal_connect(G_OBJECT(vsplit), "notify::position", G_CALLBACK(paned_position), &g_vpaned1);
|
||||
|
||||
gtk_paned_add1(GTK_PANED(vsplit), GTK_WIDGET(create_framed_widget(topleft)));
|
||||
gtk_paned_add2(GTK_PANED(vsplit), GTK_WIDGET(create_framed_widget(topright)));
|
||||
}
|
||||
{
|
||||
GtkVPaned* vsplit = GTK_VPANED(gtk_vpaned_new());
|
||||
gtk_paned_add2(GTK_PANED(hsplit), GTK_WIDGET(vsplit));
|
||||
gtk_widget_show(GTK_WIDGET(vsplit));
|
||||
|
||||
g_signal_connect(G_OBJECT(vsplit), "size_allocate", G_CALLBACK(vpaned_allocate), &g_vpaned2);
|
||||
g_signal_connect(G_OBJECT(vsplit), "notify::position", G_CALLBACK(paned_position), &g_vpaned2);
|
||||
|
||||
gtk_paned_add1(GTK_PANED(vsplit), GTK_WIDGET(create_framed_widget(botleft)));
|
||||
gtk_paned_add2(GTK_PANED(vsplit), GTK_WIDGET(create_framed_widget(botright)));
|
||||
}
|
||||
return hsplit;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue