mirror of
https://github.com/UberGames/GtkRadiant.git
synced 2024-11-29 23:22:23 +00:00
1580 lines
43 KiB
C++
1580 lines
43 KiB
C++
/*
|
|
Copyright (c) 2001, Loki software, inc.
|
|
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 name of Loki software 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 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.
|
|
*/
|
|
|
|
//
|
|
// Small functions to help with GTK
|
|
//
|
|
|
|
#include <gdk/gdkkeysyms.h>
|
|
#include <glib/gi18n.h>
|
|
|
|
#if defined ( __linux__ ) || defined ( __APPLE__ )
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#ifdef _WIN32
|
|
#include <gdk/gdkwin32.h>
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
#include <io.h>
|
|
#include <direct.h>
|
|
#define R_OK 04
|
|
#endif
|
|
#include "stdafx.h"
|
|
|
|
// =============================================================================
|
|
// Misc stuff
|
|
|
|
// NOTE TTimo window position saving has always been tricky
|
|
// it doesn't work the same between win32 and linux .. see below that code is fairly different
|
|
// it's also very poorly done, the save calls are a bit randomly disctributed in the OnDestroy
|
|
|
|
void save_window_pos( GtkWidget *wnd, window_position_t& pos ){
|
|
if ( ( wnd == NULL ) || ( wnd->window == NULL ) ) {
|
|
return;
|
|
}
|
|
|
|
get_window_pos( wnd, &pos.x, &pos.y );
|
|
|
|
pos.w = wnd->allocation.width;
|
|
pos.h = wnd->allocation.height;
|
|
|
|
#ifdef DBG_WINDOWPOS
|
|
//Sys_Printf("save_window_pos 'Window %s'\n",buf);
|
|
#endif
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
void win32_get_window_pos( GtkWidget *widget, gint *x, gint *y ){
|
|
if ( g_PrefsDlg.m_bStartOnPrimMon ) {
|
|
RECT rc;
|
|
POINT point;
|
|
HWND xwnd = (HWND)GDK_WINDOW_HWND( widget->window );
|
|
const GdkRectangle primaryMonitorRect = g_pParentWnd->GetPrimaryMonitorRect();
|
|
|
|
GetClientRect( xwnd,&rc );
|
|
point.x = rc.left;
|
|
point.y = rc.top;
|
|
ClientToScreen( xwnd,&point );
|
|
|
|
*x = point.x;
|
|
*y = point.y;
|
|
|
|
*x = max( *x,-widget->allocation.width + 10 );
|
|
*x = min( *x,primaryMonitorRect.width - 10 );
|
|
*y = max( *y,-widget->allocation.height + 10 );
|
|
*y = min( *y,primaryMonitorRect.height - 10 );
|
|
}
|
|
else {
|
|
// this is the same as the unix version of get_window_pos
|
|
gdk_window_get_root_origin( widget->window, x, y );
|
|
}
|
|
#ifdef DBG_WINDOWPOS
|
|
Sys_Printf( "win32_get_window_pos %p %d,%d\n",widget,*x,*y );
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
void load_window_pos( GtkWidget *wnd, window_position_t& pos ){
|
|
#ifdef _WIN32
|
|
const GdkRectangle primaryMonitorRect = g_pParentWnd->GetPrimaryMonitorRect();
|
|
|
|
if ( pos.x < primaryMonitorRect.x
|
|
|| pos.y < primaryMonitorRect.y
|
|
|| pos.x > primaryMonitorRect.x + primaryMonitorRect.width
|
|
|| pos.y > primaryMonitorRect.y + primaryMonitorRect.height ) {
|
|
gtk_window_set_position( GTK_WINDOW( wnd ), GTK_WIN_POS_CENTER_ON_PARENT );
|
|
}
|
|
#else
|
|
// FIXME: not multihead safe
|
|
if ( pos.x < 0
|
|
|| pos.y < 0
|
|
|| pos.x > gdk_screen_width()
|
|
|| pos.y > gdk_screen_height() ) {
|
|
gtk_window_set_position( GTK_WINDOW( wnd ), GTK_WIN_POS_CENTER_ON_PARENT );
|
|
}
|
|
#endif
|
|
else{
|
|
gtk_window_move( GTK_WINDOW( wnd ), pos.x, pos.y );
|
|
}
|
|
|
|
gtk_window_set_default_size( GTK_WINDOW( wnd ), pos.w, pos.h );
|
|
#ifdef DBG_WINDOWPOS
|
|
Sys_Printf( "load_window_pos %p 'Window,%s'\n",wnd,windowData );
|
|
#endif
|
|
}
|
|
|
|
gint widget_delete_hide( GtkWidget *widget ){
|
|
gtk_widget_hide( widget );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// Thanks to Mercury, Fingolfin - ETG
|
|
int readLongLE( FILE *file, unsigned long *m_bytesRead, int *value ){
|
|
byte buf[4];
|
|
int len = fread( buf, 4, 1, file );
|
|
*m_bytesRead += 4;
|
|
if ( len != 1 ) {
|
|
return -1;
|
|
}
|
|
|
|
*value = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
|
|
return 0;
|
|
}
|
|
|
|
short readShortLE( FILE *file, unsigned long *m_bytesRead, short unsigned *value ){
|
|
byte buf[2];
|
|
int len = fread( buf, 2, 1, file );
|
|
*m_bytesRead += 2;
|
|
if ( len != 1 ) {
|
|
return -1;
|
|
}
|
|
|
|
*value = buf[0] | buf[1] << 8;
|
|
return 0;
|
|
}
|
|
|
|
unsigned char *load_bitmap_file( const char* filename, guint16 *width, guint16 *height ){
|
|
int bmWidth, bmHeight;
|
|
short unsigned bmPlanes, bmBitsPixel;
|
|
typedef struct {
|
|
unsigned char rgbBlue;
|
|
unsigned char rgbGreen;
|
|
unsigned char rgbRed;
|
|
unsigned char rgbReserved;
|
|
} RGBQUAD;
|
|
unsigned char m1,m2;
|
|
int sizeimage;
|
|
short unsigned res1,res2;
|
|
int filesize, pixoff;
|
|
int bmisize, compression;
|
|
int xscale, yscale;
|
|
int colors, impcol;
|
|
unsigned long m_bytesRead = 0;
|
|
unsigned char *imagebits = NULL;
|
|
FILE *fp;
|
|
|
|
*width = *height = 0;
|
|
|
|
fp = fopen( filename,"rb" );
|
|
if ( fp == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
size_t rc;
|
|
rc = fread( &m1, 1, 1, fp );
|
|
m_bytesRead++;
|
|
if ( rc == -1 ) {
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
rc = fread( &m2, 1, 1, fp );
|
|
m_bytesRead++;
|
|
if ( ( m1 != 'B' ) || ( m2 != 'M' ) ) {
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
if ( readLongLE( fp,&m_bytesRead,&filesize ) ) {
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
if ( readShortLE( fp,&m_bytesRead,&res1 ) ) {
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
if ( readShortLE( fp,&m_bytesRead,&res2 ) ) {
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
if ( readLongLE( fp,&m_bytesRead,&pixoff ) ) {
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
if ( readLongLE( fp,&m_bytesRead,&bmisize ) ) {
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
if ( readLongLE( fp,&m_bytesRead,&bmWidth ) ) {
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
if ( readLongLE( fp,&m_bytesRead,&bmHeight ) ) {
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
if ( readShortLE( fp,&m_bytesRead,&bmPlanes ) ) {
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
if ( readShortLE( fp,&m_bytesRead,&bmBitsPixel ) ) {
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
if ( readLongLE( fp,&m_bytesRead,&compression ) ) {
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
if ( readLongLE( fp,&m_bytesRead,&sizeimage ) ) {
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
if ( readLongLE( fp,&m_bytesRead,&xscale ) ) {
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
if ( readLongLE( fp,&m_bytesRead,&yscale ) ) {
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
if ( readLongLE( fp,&m_bytesRead,&colors ) ) {
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
if ( readLongLE( fp,&m_bytesRead,&impcol ) ) {
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
if ( colors == 0 ) {
|
|
colors = 1 << bmBitsPixel;
|
|
}
|
|
|
|
RGBQUAD *colormap = NULL;
|
|
if ( bmBitsPixel != 24 ) {
|
|
colormap = new RGBQUAD[colors];
|
|
if ( colormap == NULL ) {
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
int i;
|
|
for ( i = 0; i < colors; i++ )
|
|
{
|
|
unsigned char r,g, b, dummy;
|
|
|
|
rc = fread( &b, 1, 1, fp );
|
|
m_bytesRead++;
|
|
if ( rc != 1 ) {
|
|
delete [] colormap;
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
rc = fread( &g, 1, 1, fp );
|
|
m_bytesRead++;
|
|
if ( rc != 1 ) {
|
|
delete [] colormap;
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
rc = fread( &r, 1, 1, fp );
|
|
m_bytesRead++;
|
|
if ( rc != 1 ) {
|
|
delete [] colormap;
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
rc = fread( &dummy, 1, 1, fp );
|
|
m_bytesRead++;
|
|
if ( rc != 1 ) {
|
|
delete [] colormap;
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
colormap[i].rgbRed = r;
|
|
colormap[i].rgbGreen = g;
|
|
colormap[i].rgbBlue = b;
|
|
}
|
|
}
|
|
|
|
if ( (long)m_bytesRead > pixoff ) {
|
|
delete [] colormap;
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
|
|
while ( (long)m_bytesRead < pixoff )
|
|
{
|
|
char dummy;
|
|
fread( &dummy,1,1,fp );
|
|
m_bytesRead++;
|
|
}
|
|
|
|
int w = bmWidth;
|
|
int h = bmHeight;
|
|
|
|
// set the output params
|
|
imagebits = (unsigned char *)malloc( w * h * 3 );
|
|
long row_size = w * 3;
|
|
|
|
if ( imagebits != NULL ) {
|
|
*width = w;
|
|
*height = h;
|
|
unsigned char *outbuf = imagebits;
|
|
long row = 0;
|
|
long rowOffset = 0;
|
|
|
|
if ( compression == 0 ) { // BI_RGB
|
|
// read rows in reverse order
|
|
for ( row = bmHeight - 1; row >= 0; row-- )
|
|
{
|
|
// which row are we working on?
|
|
rowOffset = (long unsigned)row * row_size;
|
|
|
|
if ( bmBitsPixel == 24 ) {
|
|
for ( int col = 0; col < w; col++ )
|
|
{
|
|
long offset = col * 3;
|
|
char pixel[3];
|
|
|
|
if ( fread( (void *)( pixel ),1,3,fp ) == 3 ) {
|
|
// we swap red and blue here
|
|
*( outbuf + rowOffset + offset + 0 ) = pixel[2]; // r
|
|
*( outbuf + rowOffset + offset + 1 ) = pixel[1]; // g
|
|
*( outbuf + rowOffset + offset + 2 ) = pixel[0]; // b
|
|
}
|
|
}
|
|
m_bytesRead += row_size;
|
|
|
|
// read DWORD padding
|
|
while ( ( m_bytesRead - pixoff ) & 3 )
|
|
{
|
|
char dummy;
|
|
if ( fread( &dummy,1,1,fp ) != 1 ) {
|
|
free( imagebits );
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
m_bytesRead++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// pixels are packed as 1 , 4 or 8 bit vals. need to unpack them
|
|
int bit_count = 0;
|
|
unsigned long mask = ( 1 << bmBitsPixel ) - 1;
|
|
unsigned char inbyte = 0;
|
|
|
|
for ( int col = 0; col < w; col++ )
|
|
{
|
|
int pix = 0;
|
|
|
|
// if we need another byte
|
|
if ( bit_count <= 0 ) {
|
|
bit_count = 8;
|
|
if ( fread( &inbyte,1,1,fp ) != 1 ) {
|
|
free( imagebits );
|
|
delete [] colormap;
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
m_bytesRead++;
|
|
}
|
|
|
|
// keep track of where we are in the bytes
|
|
bit_count -= bmBitsPixel;
|
|
pix = ( inbyte >> bit_count ) & mask;
|
|
|
|
// lookup the color from the colormap - stuff it in our buffer
|
|
// swap red and blue
|
|
*( outbuf + rowOffset + col * 3 + 2 ) = colormap[pix].rgbBlue;
|
|
*( outbuf + rowOffset + col * 3 + 1 ) = colormap[pix].rgbGreen;
|
|
*( outbuf + rowOffset + col * 3 + 0 ) = colormap[pix].rgbRed;
|
|
}
|
|
|
|
// read DWORD padding
|
|
while ( ( m_bytesRead - pixoff ) & 3 )
|
|
{
|
|
char dummy;
|
|
if ( fread( &dummy,1,1,fp ) != 1 ) {
|
|
free( imagebits );
|
|
if ( colormap ) {
|
|
delete [] colormap;
|
|
}
|
|
fclose( fp );
|
|
return NULL;
|
|
}
|
|
m_bytesRead++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int i, x = 0;
|
|
unsigned char c, c1 = 0, *pp;
|
|
row = 0;
|
|
pp = outbuf + ( bmHeight - 1 ) * bmWidth * 3;
|
|
|
|
if ( bmBitsPixel == 8 ) {
|
|
while ( row < bmHeight )
|
|
{
|
|
c = getc( fp );
|
|
|
|
if ( c ) {
|
|
// encoded mode
|
|
c1 = getc( fp );
|
|
for ( i = 0; i < c; x++, i++ )
|
|
{
|
|
*pp = colormap[c1].rgbRed; pp++;
|
|
*pp = colormap[c1].rgbGreen; pp++;
|
|
*pp = colormap[c1].rgbBlue; pp++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// c==0x00, escape codes
|
|
c = getc( fp );
|
|
if ( c == 0x00 ) { // end of line
|
|
row++;
|
|
x = 0;
|
|
pp = outbuf + ( bmHeight - row - 1 ) * bmWidth * 3;
|
|
}
|
|
else if ( c == 0x01 ) {
|
|
break; // end of pic
|
|
}
|
|
else if ( c == 0x02 ) { // delta
|
|
c = getc( fp );
|
|
x += c;
|
|
c = getc( fp );
|
|
row += c;
|
|
pp = outbuf + x * 3 + ( bmHeight - row - 1 ) * bmWidth * 3;
|
|
}
|
|
else // absolute mode
|
|
{
|
|
for ( i = 0; i < c; x++, i++ )
|
|
{
|
|
c1 = getc( fp );
|
|
*pp = colormap[c1].rgbRed; pp++;
|
|
*pp = colormap[c1].rgbGreen; pp++;
|
|
*pp = colormap[c1].rgbBlue; pp++;
|
|
}
|
|
|
|
if ( c & 1 ) {
|
|
getc( fp ); // odd length run: read an extra pad byte
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ( bmBitsPixel == 4 ) {
|
|
while ( row < bmHeight )
|
|
{
|
|
c = getc( fp );
|
|
|
|
if ( c ) {
|
|
// encoded mode
|
|
c1 = getc( fp );
|
|
for ( i = 0; i < c; x++, i++ )
|
|
{
|
|
*pp = colormap[( i & 1 ) ? ( c1 & 0x0f ) : ( ( c1 >> 4 ) & 0x0f )].rgbRed; pp++;
|
|
*pp = colormap[( i & 1 ) ? ( c1 & 0x0f ) : ( ( c1 >> 4 ) & 0x0f )].rgbGreen; pp++;
|
|
*pp = colormap[( i & 1 ) ? ( c1 & 0x0f ) : ( ( c1 >> 4 ) & 0x0f )].rgbBlue; pp++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// c==0x00, escape codes
|
|
c = getc( fp );
|
|
|
|
if ( c == 0x00 ) { // end of line
|
|
row++;
|
|
x = 0;
|
|
pp = outbuf + ( bmHeight - row - 1 ) * bmWidth * 3;
|
|
}
|
|
else if ( c == 0x01 ) {
|
|
break; // end of pic
|
|
}
|
|
else if ( c == 0x02 ) { // delta
|
|
c = getc( fp );
|
|
x += c;
|
|
c = getc( fp );
|
|
row += c;
|
|
pp = outbuf + x * 3 + ( bmHeight - row - 1 ) * bmWidth * 3;
|
|
}
|
|
else // absolute mode
|
|
{
|
|
for ( i = 0; i < c; x++, i++ )
|
|
{
|
|
if ( ( i & 1 ) == 0 ) {
|
|
c1 = getc( fp );
|
|
}
|
|
*pp = colormap[( i & 1 ) ? ( c1 & 0x0f ) : ( ( c1 >> 4 ) & 0x0f )].rgbRed; pp++;
|
|
*pp = colormap[( i & 1 ) ? ( c1 & 0x0f ) : ( ( c1 >> 4 ) & 0x0f )].rgbGreen; pp++;
|
|
*pp = colormap[( i & 1 ) ? ( c1 & 0x0f ) : ( ( c1 >> 4 ) & 0x0f )].rgbBlue; pp++;
|
|
}
|
|
|
|
if ( ( ( c & 3 ) == 1 ) || ( ( c & 3 ) == 2 ) ) {
|
|
getc( fp ); // odd length run: read an extra pad byte
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ( colormap ) {
|
|
delete [] colormap;
|
|
}
|
|
|
|
fclose( fp );
|
|
}
|
|
return imagebits;
|
|
}
|
|
|
|
void bmp_to_pixmap( const char* filename, GdkPixmap **pixmap, GdkBitmap **mask ){
|
|
guint16 width, height;
|
|
unsigned char *buf;
|
|
GdkWindow *window = gdk_get_default_root_window();
|
|
GdkColormap *colormap;
|
|
GdkGC* gc = gdk_gc_new( window );
|
|
int i, j;
|
|
bool hasMask = false;
|
|
|
|
*pixmap = *mask = NULL;
|
|
buf = load_bitmap_file( filename, &width, &height );
|
|
if ( !buf ) {
|
|
return;
|
|
}
|
|
|
|
colormap = gdk_drawable_get_colormap( window );
|
|
*pixmap = gdk_pixmap_new( window, width, height, -1 );
|
|
|
|
typedef struct
|
|
{
|
|
GdkColor c;
|
|
unsigned char *p;
|
|
} PAL;
|
|
|
|
for ( i = 0; i < height; i++ )
|
|
{
|
|
for ( j = 0; j < width; j++ )
|
|
{
|
|
unsigned char *p = &buf[( i * width + j ) * 3];
|
|
PAL pe;
|
|
|
|
pe.c.red = (gushort)( p[0] * 0xFF );
|
|
pe.c.green = (gushort)( p[1] * 0xFF );
|
|
pe.c.blue = (gushort)( p[2] * 0xFF );
|
|
gdk_colormap_alloc_color( colormap, &pe.c, FALSE, TRUE );
|
|
gdk_gc_set_foreground( gc, &pe.c );
|
|
gdk_draw_point( *pixmap, gc, j, i );
|
|
|
|
if ( p[0] == 0xFF && p[1] == 0x00 && p[2] == 0xFF ) {
|
|
hasMask = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
gdk_gc_unref( gc );
|
|
*mask = gdk_pixmap_new( window, width, height, 1 );
|
|
gc = gdk_gc_new( *mask );
|
|
if ( hasMask ) {
|
|
for ( i = 0; i < height; i++ )
|
|
{
|
|
for ( j = 0; j < width; j++ )
|
|
{
|
|
GdkColor mask_pattern;
|
|
|
|
// pink is transparent
|
|
if ( ( buf[( i * width + j ) * 3] == 0xff ) &&
|
|
( buf[( i * width + j ) * 3 + 1] == 0x00 ) &&
|
|
( buf[( i * width + j ) * 3 + 2] == 0xff ) ) {
|
|
mask_pattern.pixel = 0;
|
|
}
|
|
else{
|
|
mask_pattern.pixel = 1;
|
|
}
|
|
|
|
gdk_gc_set_foreground( gc, &mask_pattern );
|
|
// possible Win32 Gtk bug here
|
|
//gdk_draw_point (*mask, gc, j, i);
|
|
gdk_draw_line( *mask, gc, j, i, j + 1, i );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GdkColor mask_pattern;
|
|
mask_pattern.pixel = 1;
|
|
gdk_gc_set_foreground( gc, &mask_pattern );
|
|
gdk_draw_rectangle( *mask, gc, 1, 0, 0, width, height );
|
|
}
|
|
gdk_gc_unref( gc );
|
|
free( buf );
|
|
}
|
|
|
|
void load_pixmap( const char* filename, GtkWidget* widget, GdkPixmap **gdkpixmap, GdkBitmap **mask ){
|
|
CString str;
|
|
|
|
str = g_strBitmapsPath;
|
|
str += filename;
|
|
|
|
bmp_to_pixmap( str.GetBuffer(), gdkpixmap, mask );
|
|
if ( *gdkpixmap == NULL ) {
|
|
printf( "gdkpixmap was null\n" );
|
|
gchar *dummy[] = { "1 1 1 1", " c None", " " };
|
|
printf( "calling gdk_pixmap_create_from_xpm_d\n" );
|
|
*gdkpixmap = gdk_pixmap_create_from_xpm_d( gdk_get_default_root_window(), mask, NULL, dummy );
|
|
}
|
|
}
|
|
|
|
// this is the same as above but used by the plugins
|
|
// GdkPixmap **gdkpixmap, GdkBitmap **mask
|
|
bool WINAPI load_plugin_bitmap( const char* filename, void **gdkpixmap, void **mask ){
|
|
CString str;
|
|
|
|
str = g_strGameToolsPath;
|
|
str += g_strPluginsDir;
|
|
str += "bitmaps/";
|
|
str += filename;
|
|
bmp_to_pixmap( str.GetBuffer(), (GdkPixmap **)gdkpixmap, (GdkBitmap **)mask );
|
|
|
|
if ( *gdkpixmap == NULL ) {
|
|
// look in the core plugins
|
|
str = g_strAppPath;
|
|
str += g_strPluginsDir;
|
|
str += "bitmaps/";
|
|
str += filename;
|
|
bmp_to_pixmap( str.GetBuffer(), (GdkPixmap **)gdkpixmap, (GdkBitmap **)mask );
|
|
|
|
if ( *gdkpixmap == NULL ) {
|
|
|
|
// look in core modules
|
|
str = g_strAppPath;
|
|
str += g_strModulesDir;
|
|
str += "bitmaps/";
|
|
str += filename;
|
|
bmp_to_pixmap( str.GetBuffer(), (GdkPixmap **)gdkpixmap, (GdkBitmap **)mask );
|
|
|
|
if ( *gdkpixmap == NULL ) {
|
|
gchar *dummy[] = { "1 1 1 1", " c None", " " };
|
|
*gdkpixmap = gdk_pixmap_create_from_xpm_d( gdk_get_default_root_window(), (GdkBitmap **)mask, NULL, dummy );
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Load a xpm file and return a pixmap widget.
|
|
GtkWidget* new_pixmap( GtkWidget* widget, const char* filename ){
|
|
GdkPixmap *gdkpixmap;
|
|
GdkBitmap *mask;
|
|
GtkWidget *pixmap;
|
|
|
|
load_pixmap( filename, widget, &gdkpixmap, &mask );
|
|
pixmap = gtk_pixmap_new( gdkpixmap, mask );
|
|
|
|
gdk_drawable_unref( gdkpixmap );
|
|
gdk_drawable_unref( mask );
|
|
|
|
return pixmap;
|
|
}
|
|
|
|
// =============================================================================
|
|
// Menu stuff
|
|
|
|
GtkWidget* menu_separator( GtkWidget *menu ){
|
|
GtkWidget *menu_item = gtk_menu_item_new();
|
|
gtk_menu_append( GTK_MENU( menu ), menu_item );
|
|
gtk_widget_set_sensitive( menu_item, FALSE );
|
|
gtk_widget_show( menu_item );
|
|
return menu_item;
|
|
}
|
|
|
|
GtkWidget* menu_tearoff( GtkWidget *menu ){
|
|
GtkWidget *menu_item = gtk_tearoff_menu_item_new();
|
|
gtk_menu_append( GTK_MENU( menu ), menu_item );
|
|
// gtk_widget_set_sensitive (menu_item, FALSE); -- controls whether menu is detachable
|
|
gtk_widget_show( menu_item );
|
|
return menu_item;
|
|
}
|
|
|
|
GtkWidget* create_sub_menu_with_mnemonic( GtkWidget *bar, const gchar *mnemonic ){
|
|
GtkWidget *item, *sub_menu;
|
|
|
|
item = gtk_menu_item_new_with_mnemonic( mnemonic );
|
|
gtk_widget_show( item );
|
|
gtk_container_add( GTK_CONTAINER( bar ), item );
|
|
|
|
sub_menu = gtk_menu_new();
|
|
gtk_menu_item_set_submenu( GTK_MENU_ITEM( item ), sub_menu );
|
|
|
|
return sub_menu;
|
|
}
|
|
|
|
extern void AddMenuItem( GtkWidget* menu, unsigned int id );
|
|
|
|
GtkWidget* create_menu_item_with_mnemonic( GtkWidget *menu, const gchar *mnemonic, GtkSignalFunc func, int id ){
|
|
GtkWidget *item;
|
|
|
|
item = gtk_menu_item_new_with_mnemonic( mnemonic );
|
|
|
|
gtk_widget_show( item );
|
|
gtk_container_add( GTK_CONTAINER( menu ), item );
|
|
gtk_signal_connect( GTK_OBJECT( item ), "activate", GTK_SIGNAL_FUNC( func ), GINT_TO_POINTER( id ) );
|
|
|
|
AddMenuItem( item, id );
|
|
return item;
|
|
}
|
|
|
|
GtkWidget* create_check_menu_item_with_mnemonic( GtkWidget *menu, const gchar *mnemonic, GtkSignalFunc func, int id, gboolean active ){
|
|
GtkWidget *item;
|
|
|
|
item = gtk_check_menu_item_new_with_mnemonic( mnemonic );
|
|
|
|
gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( item ), active );
|
|
gtk_widget_show( item );
|
|
gtk_container_add( GTK_CONTAINER( menu ), item );
|
|
gtk_signal_connect( GTK_OBJECT( item ), "activate", GTK_SIGNAL_FUNC( func ), GINT_TO_POINTER( id ) );
|
|
|
|
AddMenuItem( item, id );
|
|
return item;
|
|
}
|
|
|
|
GtkWidget* create_radio_menu_item_with_mnemonic( GtkWidget *menu, GtkWidget *last, const gchar *mnemonic, GtkSignalFunc func, int id, gboolean state ){
|
|
GtkWidget *item;
|
|
GSList *group = (GSList*)NULL;
|
|
|
|
if ( last != NULL ) {
|
|
group = gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( last ) );
|
|
}
|
|
item = gtk_radio_menu_item_new_with_mnemonic( group, mnemonic );
|
|
gtk_check_menu_item_set_state( GTK_CHECK_MENU_ITEM( item ), state );
|
|
|
|
gtk_widget_show( item );
|
|
gtk_container_add( GTK_CONTAINER( menu ), item );
|
|
gtk_signal_connect( GTK_OBJECT( item ), "activate", GTK_SIGNAL_FUNC( func ), GINT_TO_POINTER( id ) );
|
|
|
|
AddMenuItem( item, id );
|
|
return item;
|
|
}
|
|
|
|
GtkWidget* create_menu_in_menu_with_mnemonic( GtkWidget *menu, const gchar *mnemonic ){
|
|
GtkWidget *item, *submenu;
|
|
|
|
item = gtk_menu_item_new_with_mnemonic( mnemonic );
|
|
gtk_widget_show( item );
|
|
gtk_container_add( GTK_CONTAINER( menu ), item );
|
|
|
|
submenu = gtk_menu_new();
|
|
gtk_menu_item_set_submenu( GTK_MENU_ITEM( item ), submenu );
|
|
|
|
return submenu;
|
|
}
|
|
|
|
// =============================================================================
|
|
// Message Boxes
|
|
|
|
void dialog_button_callback( GtkWidget *widget, gpointer data ) {
|
|
GtkWidget *parent;
|
|
int *loop, *ret;
|
|
|
|
parent = gtk_widget_get_toplevel( widget );
|
|
loop = (int*)g_object_get_data( G_OBJECT( parent ), "loop" );
|
|
ret = (int*)g_object_get_data( G_OBJECT( parent ), "ret" );
|
|
|
|
*loop = 0;
|
|
*ret = GPOINTER_TO_INT( data );
|
|
}
|
|
|
|
gint dialog_delete_callback( GtkWidget *widget, GdkEvent* event, gpointer data ){
|
|
int *loop;
|
|
|
|
gtk_widget_hide( widget );
|
|
loop = (int*)g_object_get_data( G_OBJECT( widget ), "loop" );
|
|
*loop = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gint dialog_url_callback( GtkWidget *widget, GdkEvent* event, gpointer data ){
|
|
OpenURL( (const char *)g_object_get_data( G_OBJECT( widget ), "URL" ) );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int WINAPI gtk_MessageBox( void *parent, const char* lpText, const char* lpCaption, guint32 uType, const char* URL ){
|
|
GtkWidget *window, *w, *vbox, *hbox;
|
|
GtkAccelGroup *accel;
|
|
int mode = ( uType & MB_TYPEMASK ), ret, loop = 1;
|
|
|
|
window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
|
|
gtk_signal_connect( GTK_OBJECT( window ), "delete_event",
|
|
GTK_SIGNAL_FUNC( dialog_delete_callback ), NULL );
|
|
gtk_signal_connect( GTK_OBJECT( window ), "destroy",
|
|
GTK_SIGNAL_FUNC( gtk_widget_destroy ), NULL );
|
|
gtk_window_set_title( GTK_WINDOW( window ), lpCaption );
|
|
gtk_container_border_width( GTK_CONTAINER( window ), 10 );
|
|
g_object_set_data( G_OBJECT( window ), "loop", &loop );
|
|
g_object_set_data( G_OBJECT( window ), "ret", &ret );
|
|
gtk_widget_realize( window );
|
|
|
|
gtk_window_set_policy( GTK_WINDOW( window ),FALSE,FALSE,TRUE );
|
|
|
|
if ( parent != NULL ) {
|
|
gtk_window_set_transient_for( GTK_WINDOW( window ), GTK_WINDOW( parent ) );
|
|
}
|
|
|
|
accel = gtk_accel_group_new();
|
|
gtk_window_add_accel_group( GTK_WINDOW( window ), accel );
|
|
|
|
vbox = gtk_vbox_new( FALSE, 10 );
|
|
gtk_container_add( GTK_CONTAINER( window ), vbox );
|
|
gtk_widget_show( vbox );
|
|
|
|
w = gtk_label_new( lpText );
|
|
gtk_box_pack_start( GTK_BOX( vbox ), w, FALSE, FALSE, 2 );
|
|
gtk_label_set_justify( GTK_LABEL( w ), GTK_JUSTIFY_LEFT );
|
|
gtk_widget_show( w );
|
|
|
|
w = gtk_hseparator_new();
|
|
gtk_box_pack_start( GTK_BOX( vbox ), w, FALSE, FALSE, 2 );
|
|
gtk_widget_show( w );
|
|
|
|
hbox = gtk_hbox_new( FALSE, 10 );
|
|
gtk_box_pack_start( GTK_BOX( vbox ), hbox, FALSE, FALSE, 2 );
|
|
gtk_widget_show( hbox );
|
|
|
|
if ( mode == MB_OK ) {
|
|
w = gtk_button_new_with_label( _( "Ok" ) );
|
|
gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
|
|
gtk_signal_connect( GTK_OBJECT( w ), "clicked",
|
|
GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDOK ) );
|
|
gtk_widget_add_accelerator( w, "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
|
|
gtk_widget_add_accelerator( w, "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
|
|
GTK_WIDGET_SET_FLAGS( w, GTK_CAN_DEFAULT );
|
|
gtk_widget_grab_default( w );
|
|
gtk_widget_show( w );
|
|
ret = IDOK;
|
|
}
|
|
else if ( mode == MB_OKCANCEL ) {
|
|
w = gtk_button_new_with_label( _( "Ok" ) );
|
|
gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
|
|
gtk_signal_connect( GTK_OBJECT( w ), "clicked",
|
|
GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDOK ) );
|
|
gtk_widget_add_accelerator( w, "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
|
|
GTK_WIDGET_SET_FLAGS( w, GTK_CAN_DEFAULT );
|
|
gtk_widget_grab_default( w );
|
|
gtk_widget_show( w );
|
|
|
|
w = gtk_button_new_with_label( _( "Cancel" ) );
|
|
gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
|
|
gtk_signal_connect( GTK_OBJECT( w ), "clicked",
|
|
GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDCANCEL ) );
|
|
gtk_widget_add_accelerator( w, "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
|
|
gtk_widget_show( w );
|
|
ret = IDCANCEL;
|
|
}
|
|
else if ( mode == MB_YESNOCANCEL ) {
|
|
w = gtk_button_new_with_label( _( "Yes" ) );
|
|
gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
|
|
gtk_signal_connect( GTK_OBJECT( w ), "clicked",
|
|
GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDYES ) );
|
|
GTK_WIDGET_SET_FLAGS( w, GTK_CAN_DEFAULT );
|
|
gtk_widget_grab_default( w );
|
|
gtk_widget_show( w );
|
|
|
|
w = gtk_button_new_with_label( _( "No" ) );
|
|
gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
|
|
gtk_signal_connect( GTK_OBJECT( w ), "clicked",
|
|
GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDNO ) );
|
|
gtk_widget_show( w );
|
|
|
|
w = gtk_button_new_with_label( _( "Cancel" ) );
|
|
gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
|
|
gtk_signal_connect( GTK_OBJECT( w ), "clicked",
|
|
GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDCANCEL ) );
|
|
gtk_widget_show( w );
|
|
ret = IDCANCEL;
|
|
}
|
|
else /* if (mode == MB_YESNO) */
|
|
{
|
|
w = gtk_button_new_with_label( _( "Yes" ) );
|
|
gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
|
|
gtk_signal_connect( GTK_OBJECT( w ), "clicked",
|
|
GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDYES ) );
|
|
GTK_WIDGET_SET_FLAGS( w, GTK_CAN_DEFAULT );
|
|
gtk_widget_grab_default( w );
|
|
gtk_widget_show( w );
|
|
|
|
w = gtk_button_new_with_label( _( "No" ) );
|
|
gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
|
|
gtk_signal_connect( GTK_OBJECT( w ), "clicked",
|
|
GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDNO ) );
|
|
gtk_widget_show( w );
|
|
ret = IDNO;
|
|
}
|
|
|
|
if ( URL ) {
|
|
w = gtk_button_new_with_label( _( "Go to URL" ) );
|
|
gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
|
|
gtk_signal_connect( GTK_OBJECT( w ), "clicked",
|
|
GTK_SIGNAL_FUNC( dialog_url_callback ), NULL );
|
|
g_object_set_data( G_OBJECT( w ), "URL", (void *)URL );
|
|
GTK_WIDGET_SET_FLAGS( w, GTK_CAN_DEFAULT );
|
|
gtk_widget_grab_default( w );
|
|
gtk_widget_show( w );
|
|
}
|
|
|
|
|
|
gtk_widget_show( window );
|
|
gtk_grab_add( window );
|
|
|
|
while ( loop )
|
|
gtk_main_iteration();
|
|
|
|
gtk_grab_remove( window );
|
|
gtk_widget_destroy( window );
|
|
|
|
return ret;
|
|
}
|
|
|
|
// =============================================================================
|
|
// File dialog
|
|
|
|
// fenris #3078 WHENHELLISFROZENOVER
|
|
|
|
static void file_sel_callback( GtkWidget *widget, gpointer data ){
|
|
GtkWidget *parent;
|
|
int *loop;
|
|
bool *success;
|
|
|
|
parent = gtk_widget_get_toplevel( widget );
|
|
loop = (int*)g_object_get_data( G_OBJECT( parent ), "loop" );
|
|
success = (bool*)g_object_get_data( G_OBJECT( parent ), "success" );
|
|
|
|
if ( GPOINTER_TO_INT( data ) == IDOK ) {
|
|
*success = true;
|
|
}
|
|
|
|
*loop = 0;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
#include <commdlg.h>
|
|
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 */
|
|
#else
|
|
static char szFile[QER_MAX_NAMELEN];
|
|
#endif
|
|
|
|
#define FILEDLG_CUSTOM_FILTER_LENGTH 64
|
|
// to be used with the advanced file selector
|
|
|
|
class CFileType : public IFileTypeList
|
|
{
|
|
struct filetype_copy_t
|
|
{
|
|
void operator=( const filetype_t& other ){
|
|
m_name = other.name;
|
|
m_pattern = other.pattern;
|
|
}
|
|
string_t m_name;
|
|
string_t m_pattern;
|
|
};
|
|
public:
|
|
CFileType(){
|
|
m_nTypes = 0;
|
|
m_pTypes = NULL;
|
|
m_strWin32Filters = NULL;
|
|
m_pstrGTKMasks = NULL;
|
|
}
|
|
|
|
virtual ~CFileType(){
|
|
delete[] m_pTypes;
|
|
DestroyWin32Filters();
|
|
DestroyGTKMasks();
|
|
}
|
|
|
|
void addType( filetype_t type ){
|
|
filetype_copy_t* newTypes = new filetype_copy_t [m_nTypes + 1];
|
|
if ( m_nTypes > 0 ) {
|
|
for ( int i = 0; i < m_nTypes; i++ )
|
|
newTypes[i] = m_pTypes[i];
|
|
delete[] m_pTypes;
|
|
}
|
|
m_pTypes = newTypes;
|
|
m_pTypes[m_nTypes] = type;
|
|
m_nTypes++;
|
|
ConstructGTKMasks();
|
|
ConstructWin32Filters();
|
|
}
|
|
|
|
filetype_t GetTypeForWin32Filter( const char *filter ) const {
|
|
for ( int i = 0; i < m_nTypes; i++ )
|
|
if ( strcmp( m_pTypes[i].m_pattern.c_str(), filter ) == 0 ) {
|
|
return filetype_t( m_pTypes[i].m_name.c_str(), m_pTypes[i].m_pattern.c_str() );
|
|
}
|
|
return filetype_t();
|
|
}
|
|
|
|
filetype_t GetTypeForGTKMask( const char *mask ) const {
|
|
for ( int i = 0; i < m_nTypes; i++ )
|
|
if ( strcmp( m_pstrGTKMasks[i],mask ) == 0 ) {
|
|
return filetype_t( m_pTypes[i].m_name.c_str(), m_pTypes[i].m_pattern.c_str() );
|
|
}
|
|
return filetype_t();
|
|
}
|
|
|
|
int GetNumTypes(){
|
|
return m_nTypes;
|
|
}
|
|
|
|
filetype_t GetTypeForIndex( int index ) const // Zero-based index.
|
|
{
|
|
if ( index >= 0 && index < m_nTypes ) {
|
|
return filetype_t( m_pTypes[index].m_name.c_str(), m_pTypes[index].m_pattern.c_str() );
|
|
}
|
|
return filetype_t();
|
|
}
|
|
|
|
char *m_strWin32Filters;
|
|
char **m_pstrGTKMasks;
|
|
private:
|
|
int m_nTypes;
|
|
filetype_copy_t *m_pTypes;
|
|
|
|
void DestroyWin32Filters(){
|
|
delete[] m_strWin32Filters;
|
|
}
|
|
|
|
void ConstructWin32Filters(){
|
|
const char *r;
|
|
char *w;
|
|
int i;
|
|
int len = 0;
|
|
DestroyWin32Filters();
|
|
for ( i = 0; i < m_nTypes; i++ )
|
|
len = len + strlen( m_pTypes[i].m_name.c_str() ) + strlen( m_pTypes[i].m_pattern.c_str() ) * 2 + 5;
|
|
m_strWin32Filters = new char[len + 1]; // length + null char
|
|
for ( i = 0, w = m_strWin32Filters; i < m_nTypes; i++ )
|
|
{
|
|
for ( r = m_pTypes[i].m_name.c_str(); *r != '\0'; r++, w++ )
|
|
*w = *r;
|
|
*w++ = ' ';
|
|
*w++ = '(';
|
|
for ( r = m_pTypes[i].m_pattern.c_str(); *r != '\0'; r++, w++ )
|
|
*w = *r;
|
|
*w++ = ')';
|
|
*w++ = '\0';
|
|
for ( r = m_pTypes[i].m_pattern.c_str(); *r != '\0'; r++, w++ )
|
|
*w = ( *r == ',' ) ? ';' : *r;
|
|
*w++ = '\0';
|
|
}
|
|
m_strWin32Filters[len] = '\0';
|
|
}
|
|
|
|
void DestroyGTKMasks(){
|
|
if ( m_pstrGTKMasks != NULL ) {
|
|
for ( char **p = m_pstrGTKMasks; *p != NULL; p++ )
|
|
delete[] *p;
|
|
}
|
|
delete[] m_pstrGTKMasks;
|
|
}
|
|
|
|
void ConstructGTKMasks(){
|
|
const char *r;
|
|
char *w;
|
|
int i;
|
|
int len = 0;
|
|
DestroyGTKMasks();
|
|
m_pstrGTKMasks = new char*[m_nTypes + 1];
|
|
for ( i = 0; i < m_nTypes; i++ )
|
|
{
|
|
len = strlen( m_pTypes[i].m_name.c_str() ) + strlen( m_pTypes[i].m_pattern.c_str() ) + 3;
|
|
m_pstrGTKMasks[i] = new char[len + 1]; // length + null char
|
|
w = m_pstrGTKMasks[i];
|
|
for ( r = m_pTypes[i].m_name.c_str(); *r != '\0'; r++, w++ )
|
|
*w = *r;
|
|
*w++ = ' ';
|
|
*w++ = '(';
|
|
for ( r = m_pTypes[i].m_pattern.c_str(); *r != '\0'; r++, w++ )
|
|
*w = *r;
|
|
*w++ = ')';
|
|
*w++ = '\0';
|
|
}
|
|
m_pstrGTKMasks[m_nTypes] = NULL;
|
|
}
|
|
|
|
};
|
|
|
|
#ifdef _WIN32
|
|
|
|
typedef struct {
|
|
gboolean open;
|
|
OPENFILENAME *ofn;
|
|
BOOL dlgRtnVal;
|
|
bool done;
|
|
} win32_native_file_dialog_comms_t;
|
|
|
|
DWORD WINAPI win32_native_file_dialog_thread_func( LPVOID lpParam ){
|
|
win32_native_file_dialog_comms_t *fileDialogComms;
|
|
fileDialogComms = (win32_native_file_dialog_comms_t *) lpParam;
|
|
if ( fileDialogComms->open ) {
|
|
fileDialogComms->dlgRtnVal = GetOpenFileName( fileDialogComms->ofn );
|
|
}
|
|
else {
|
|
fileDialogComms->dlgRtnVal = GetSaveFileName( fileDialogComms->ofn );
|
|
}
|
|
fileDialogComms->done = true; // No need to synchronize around lock, one-way gate.
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
/**
|
|
* @param[in] baseSubDir should have a trailing slash if not @c NULL
|
|
*/
|
|
const char* file_dialog( void *parent, gboolean open, const char* title, const char* path, const char* pattern, const char *baseSubDir ){
|
|
|
|
#ifdef _WIN32
|
|
static bool in_file_dialog = false;
|
|
HANDLE fileDialogThreadHandle;
|
|
win32_native_file_dialog_comms_t fileDialogComms;
|
|
bool dialogDone;
|
|
#endif
|
|
|
|
// Gtk dialog
|
|
GtkWidget* file_sel;
|
|
char *new_path = NULL;
|
|
|
|
const char* r;
|
|
char *v, *w;
|
|
filetype_t type;
|
|
CFileType typelist;
|
|
if ( pattern != NULL ) {
|
|
GetFileTypeRegistry()->getTypeList( pattern, &typelist );
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
if ( g_PrefsDlg.m_bNativeGUI ) {
|
|
// do that the native way
|
|
|
|
if ( in_file_dialog ) {
|
|
return NULL; // Avoid recursive entry.
|
|
}
|
|
in_file_dialog = true;
|
|
/* Set the members of the OPENFILENAME structure. */
|
|
// See http://msdn.microsoft.com/en-us/library/ms646839%28v=vs.85%29.aspx .
|
|
memset( &ofn, 0, sizeof( ofn ) );
|
|
ofn.lStructSize = sizeof( ofn );
|
|
ofn.hwndOwner = (HWND)GDK_WINDOW_HWND( g_pParentWnd->m_pWidget->window );
|
|
ofn.nFilterIndex = 1; // The index is 1-based, not 0-based. This basically says,
|
|
// "select the first filter as default".
|
|
if ( pattern ) {
|
|
ofn.lpstrFilter = typelist.m_strWin32Filters;
|
|
}
|
|
else
|
|
{
|
|
// TODO: Would be a bit cleaner if we could extract this string from
|
|
// GetFileTypeRegistry() instead of hardcoding it here.
|
|
ofn.lpstrFilter = "all files\0*.*\0"; // Second '\0' will be added to end of string.
|
|
}
|
|
szFile[0] = '\0';
|
|
ofn.lpstrFile = szFile;
|
|
ofn.nMaxFile = sizeof( szFile );
|
|
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;
|
|
}
|
|
ofn.lpstrTitle = title;
|
|
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
|
|
|
|
memset( &fileDialogComms, 0, sizeof( fileDialogComms ) );
|
|
fileDialogComms.open = open;
|
|
fileDialogComms.ofn = &ofn;
|
|
|
|
fileDialogThreadHandle =
|
|
CreateThread( NULL, // lpThreadAttributes
|
|
0, // dwStackSize, default stack size
|
|
win32_native_file_dialog_thread_func, // lpStartAddress, funcion to call
|
|
&fileDialogComms, // lpParameter, argument to pass to function
|
|
0, // dwCreationFlags
|
|
NULL ); // lpThreadId
|
|
|
|
dialogDone = false;
|
|
while ( 1 ) {
|
|
// Avoid blocking indefinitely. Another thread will set fileDialogComms->done to true;
|
|
// we don't want to be in an indefinite blocked state when this happens. We want to break
|
|
// out of here eventually.
|
|
while ( gtk_events_pending() ) {
|
|
gtk_main_iteration();
|
|
}
|
|
if ( dialogDone ) {
|
|
break;
|
|
}
|
|
if ( fileDialogComms.done ) {
|
|
dialogDone = true; // One more loop of gtk_main_iteration() to get things in sync.
|
|
}
|
|
// Avoid tight infinte loop, add a small amount of sleep.
|
|
Sleep( 10 );
|
|
}
|
|
// Make absolutely sure that the thread is finished before we call CloseHandle().
|
|
WaitForSingleObject( fileDialogThreadHandle, INFINITE );
|
|
CloseHandle( fileDialogThreadHandle );
|
|
|
|
in_file_dialog = false;
|
|
|
|
if ( !fileDialogComms.dlgRtnVal ) {
|
|
return NULL; // Cancelled.
|
|
}
|
|
|
|
if ( pattern != NULL ) {
|
|
type = typelist.GetTypeForIndex( ofn.nFilterIndex - 1 );
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
char buf[PATH_MAX];
|
|
// do that the Gtk way
|
|
if ( title == NULL ) {
|
|
title = open ? _( "Open File" ) : _( "Save File" );
|
|
}
|
|
|
|
// we expect an actual path below, if the path is NULL we might crash
|
|
if ( !path || path[0] == '\0' ) {
|
|
strcpy( buf, g_pGameDescription->mEnginePath.GetBuffer() );
|
|
strcat( buf, g_pGameDescription->mBaseGame.GetBuffer() );
|
|
strcat( buf, "/" );
|
|
if ( baseSubDir ) {
|
|
strcat( buf, baseSubDir );
|
|
}
|
|
path = buf;
|
|
}
|
|
|
|
// alloc new path with extra char for dir separator
|
|
new_path = new char[strlen( path ) + 1 + 1];
|
|
// copy path, replacing dir separators as appropriate
|
|
for ( r = path, w = new_path; *r != '\0'; r++ )
|
|
*w++ = ( *r == '/' ) ? G_DIR_SEPARATOR : *r;
|
|
// add dir separator to end of path if required
|
|
if ( *( w - 1 ) != G_DIR_SEPARATOR ) {
|
|
*w++ = G_DIR_SEPARATOR;
|
|
}
|
|
// terminate string
|
|
*w = '\0';
|
|
|
|
file_sel = gtk_file_chooser_dialog_new( title,
|
|
GTK_WINDOW( parent ),
|
|
open ? GTK_FILE_CHOOSER_ACTION_OPEN : GTK_FILE_CHOOSER_ACTION_SAVE,
|
|
GTK_STOCK_CANCEL,
|
|
GTK_RESPONSE_CANCEL,
|
|
open ? GTK_STOCK_OPEN : GTK_STOCK_SAVE,
|
|
GTK_RESPONSE_ACCEPT,
|
|
NULL );
|
|
gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( file_sel ), new_path );
|
|
delete[] new_path;
|
|
|
|
// Setting the file chooser dialog to modal and centering it on the parent is done automatically.
|
|
|
|
if ( pattern != NULL ) {
|
|
for ( int i = 0; i < typelist.GetNumTypes(); i++ ) {
|
|
GtkFileFilter *filter = gtk_file_filter_new();
|
|
type = typelist.GetTypeForIndex( i );
|
|
// We can use type.name here, or m_pstrGTKMasks[i], which includes the actual pattern.
|
|
gtk_file_filter_set_name( filter, typelist.m_pstrGTKMasks[i] );
|
|
gtk_file_filter_add_pattern( filter, type.pattern );
|
|
// "Note that the chooser takes ownership of the filter, so
|
|
// you have to ref and sink it if you want to keep a reference."
|
|
// So I guess we won't need to garbage collect this.
|
|
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( file_sel ), filter );
|
|
}
|
|
}
|
|
|
|
if ( gtk_dialog_run( GTK_DIALOG( file_sel ) ) == GTK_RESPONSE_ACCEPT ) {
|
|
strcpy( szFile, gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( file_sel ) ) );
|
|
}
|
|
else {
|
|
szFile[0] = '\0';
|
|
}
|
|
|
|
if ( pattern != NULL ) {
|
|
GtkFileFilter *filter = gtk_file_chooser_get_filter( GTK_FILE_CHOOSER( file_sel ) );
|
|
if ( filter == NULL ) {
|
|
type = filetype_t();
|
|
}
|
|
else {
|
|
type = typelist.GetTypeForGTKMask( gtk_file_filter_get_name( filter ) );
|
|
}
|
|
}
|
|
gtk_widget_destroy( file_sel );
|
|
|
|
#ifdef _WIN32
|
|
}
|
|
#endif
|
|
|
|
// don't return an empty filename
|
|
if ( szFile[0] == '\0' ) {
|
|
return NULL;
|
|
}
|
|
|
|
// convert back to unix format
|
|
for ( w = szFile; *w != '\0'; w++ )
|
|
if ( *w == '\\' ) {
|
|
*w = '/';
|
|
}
|
|
|
|
/* \todo SPoG - file_dialog should return filetype information separately.. not force file extension.. */
|
|
if ( !open && pattern != NULL ) {
|
|
v = strrchr( szFile, '/' );
|
|
w = strrchr( szFile, '.' );
|
|
if ( ( v && w && w < v ) || // Last '.' is before the file.
|
|
w == NULL ) { // Extension missing.
|
|
if ( type.pattern[0] ) {
|
|
w = szFile + strlen( szFile );
|
|
strcpy( w, type.pattern + 1 ); // Add extension of selected filter type.
|
|
}
|
|
else {
|
|
// type will be empty if for example there were no filters for pattern,
|
|
// or if some other UI inconsistencies happen.
|
|
if ( gtk_MessageBox( parent, "No file extension specified in file to be saved.\nAttempt to save anyways?",
|
|
"GtkRadiant", MB_YESNO ) == IDNO ) {
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
else { // An extension was explicitly in the filename.
|
|
bool knownExtension = false;
|
|
for ( int i = typelist.GetNumTypes() - 1; i >= 0; i-- ) {
|
|
type = typelist.GetTypeForIndex( i );
|
|
if ( type.pattern[0] && strcmp( w, type.pattern + 1 ) == 0 ) {
|
|
knownExtension = true;
|
|
break;
|
|
}
|
|
}
|
|
if ( !knownExtension ) {
|
|
if ( gtk_MessageBox( parent, "Unknown file extension for this save operation.\nAttempt to save anyways?",
|
|
"GtkRadiant", MB_YESNO ) == IDNO ) {
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// prompt to overwrite existing files
|
|
if ( !open ) {
|
|
if ( access( szFile, R_OK ) == 0 ) {
|
|
if ( gtk_MessageBox( parent, "File already exists.\nOverwrite?", "GtkRadiant", MB_YESNO ) == IDNO ) {
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return szFile;
|
|
}
|
|
|
|
char* WINAPI dir_dialog( void *parent, const char* title, const char* path ){
|
|
GtkWidget* file_sel;
|
|
char* filename = (char*)NULL;
|
|
int loop = 1;
|
|
bool success = false;
|
|
|
|
file_sel = gtk_file_selection_new( title );
|
|
gtk_signal_connect( GTK_OBJECT( GTK_FILE_SELECTION( file_sel )->ok_button ), "clicked",
|
|
GTK_SIGNAL_FUNC( file_sel_callback ), GINT_TO_POINTER( IDOK ) );
|
|
gtk_signal_connect( GTK_OBJECT( GTK_FILE_SELECTION( file_sel )->cancel_button ), "clicked",
|
|
GTK_SIGNAL_FUNC( file_sel_callback ), GINT_TO_POINTER( IDCANCEL ) );
|
|
gtk_signal_connect( GTK_OBJECT( file_sel ), "delete_event",
|
|
GTK_SIGNAL_FUNC( dialog_delete_callback ), NULL );
|
|
gtk_file_selection_hide_fileop_buttons( GTK_FILE_SELECTION( file_sel ) );
|
|
|
|
if ( parent != NULL ) {
|
|
gtk_window_set_transient_for( GTK_WINDOW( file_sel ), GTK_WINDOW( parent ) );
|
|
}
|
|
|
|
gtk_widget_hide( GTK_FILE_SELECTION( file_sel )->file_list->parent );
|
|
|
|
g_object_set_data( G_OBJECT( file_sel ), "loop", &loop );
|
|
g_object_set_data( G_OBJECT( file_sel ), "success", &success );
|
|
|
|
if ( path != NULL ) {
|
|
gtk_file_selection_set_filename( GTK_FILE_SELECTION( file_sel ), path );
|
|
}
|
|
|
|
gtk_grab_add( file_sel );
|
|
gtk_widget_show( file_sel );
|
|
|
|
while ( loop )
|
|
gtk_main_iteration();
|
|
|
|
filename = g_strdup( gtk_file_selection_get_filename( GTK_FILE_SELECTION( file_sel ) ) );
|
|
|
|
gtk_grab_remove( file_sel );
|
|
gtk_widget_destroy( file_sel );
|
|
|
|
return filename;
|
|
}
|
|
|
|
bool WINAPI color_dialog( void *parent, float *color, const char* title ){
|
|
GtkWidget* dlg;
|
|
double clr[3];
|
|
int loop = 1, ret = IDCANCEL;
|
|
|
|
clr[0] = color[0];
|
|
clr[1] = color[1];
|
|
clr[2] = color[2];
|
|
|
|
dlg = gtk_color_selection_dialog_new( title );
|
|
gtk_color_selection_set_color( GTK_COLOR_SELECTION( GTK_COLOR_SELECTION_DIALOG( dlg )->colorsel ), clr );
|
|
gtk_signal_connect( GTK_OBJECT( dlg ), "delete_event",
|
|
GTK_SIGNAL_FUNC( dialog_delete_callback ), NULL );
|
|
gtk_signal_connect( GTK_OBJECT( dlg ), "destroy",
|
|
GTK_SIGNAL_FUNC( gtk_widget_destroy ), NULL );
|
|
gtk_signal_connect( GTK_OBJECT( GTK_COLOR_SELECTION_DIALOG( dlg )->ok_button ), "clicked",
|
|
GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDOK ) );
|
|
gtk_signal_connect( GTK_OBJECT( GTK_COLOR_SELECTION_DIALOG( dlg )->cancel_button ), "clicked",
|
|
GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDCANCEL ) );
|
|
g_object_set_data( G_OBJECT( dlg ), "loop", &loop );
|
|
g_object_set_data( G_OBJECT( dlg ), "ret", &ret );
|
|
|
|
if ( parent != NULL ) {
|
|
gtk_window_set_transient_for( GTK_WINDOW( dlg ), GTK_WINDOW( parent ) );
|
|
}
|
|
|
|
gtk_widget_show( dlg );
|
|
gtk_grab_add( dlg );
|
|
|
|
while ( loop )
|
|
gtk_main_iteration();
|
|
|
|
GdkColor gdkcolor;
|
|
gtk_color_selection_get_current_color( GTK_COLOR_SELECTION( GTK_COLOR_SELECTION_DIALOG( dlg )->colorsel ), &gdkcolor );
|
|
clr[0] = gdkcolor.red / 65535.0;
|
|
clr[1] = gdkcolor.green / 65535.0;
|
|
clr[2] = gdkcolor.blue / 65535.0;
|
|
|
|
gtk_grab_remove( dlg );
|
|
gtk_widget_destroy( dlg );
|
|
|
|
if ( ret == IDOK ) {
|
|
color[0] = (float)clr[0];
|
|
color[1] = (float)clr[1];
|
|
color[2] = (float)clr[2];
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void OpenURL( const char *url ){
|
|
// let's put a little comment
|
|
Sys_Printf( "OpenURL: %s\n", url );
|
|
#ifdef __linux__
|
|
// \todo FIXME: the way we open URLs on *nix should be improved. A script is good (see how I do on RTCW)
|
|
char command[2 * PATH_MAX];
|
|
snprintf( command, sizeof( command ), "%s/openurl.sh \"%s\" &", g_strAppPath.GetBuffer(), url );
|
|
if ( system( command ) != 0 ) {
|
|
gtk_MessageBox( g_pParentWnd->m_pWidget, "Failed to launch Netscape!" );
|
|
}
|
|
#endif
|
|
#ifdef __APPLE__
|
|
char command[2 * PATH_MAX];
|
|
snprintf( command, sizeof( command ),
|
|
"open \"%s\" &", url, url );
|
|
if ( system( command ) != 0 ) {
|
|
gtk_MessageBox( g_pParentWnd->m_pWidget, "Unable to launch browser!" );
|
|
}
|
|
#endif
|
|
#ifdef _WIN32
|
|
ShellExecute( (HWND)GDK_WINDOW_HWND( g_pParentWnd->m_pWidget->window ), "open", url, NULL, NULL, SW_SHOW );
|
|
#endif
|
|
}
|
|
|
|
void CheckMenuSplitting( GtkWidget *&menu ){
|
|
GtkWidget *item,*menu2;
|
|
|
|
GtkRequisition requisition;
|
|
gint screen_height;
|
|
|
|
gtk_widget_size_request( GTK_WIDGET( menu ), &requisition );
|
|
screen_height = gdk_screen_height();
|
|
|
|
if ( ( screen_height - requisition.height ) < 20 ) {
|
|
menu2 = gtk_menu_new();
|
|
|
|
// move the last 2 items to a submenu (3 because of win32)
|
|
for ( int i = 0; i < 3; i++ )
|
|
{
|
|
item = GTK_WIDGET( g_list_last( gtk_container_children( GTK_CONTAINER( menu ) ) )->data );
|
|
gtk_widget_ref( item );
|
|
gtk_container_remove( GTK_CONTAINER( menu ), item );
|
|
gtk_menu_append( GTK_MENU( menu2 ), item );
|
|
gtk_widget_unref( item );
|
|
}
|
|
|
|
item = gtk_menu_item_new_with_label( "--------" );
|
|
gtk_widget_show( item );
|
|
gtk_container_add( GTK_CONTAINER( menu ), item );
|
|
gtk_menu_item_set_submenu( GTK_MENU_ITEM( item ), menu2 );
|
|
menu = menu2;
|
|
}
|
|
}
|