mirror of
https://github.com/UberGames/GtkRadiant.git
synced 2024-11-27 22:22:22 +00:00
36a1c4bbad
Just some code maintenence. Like removing unused local vars. PLS MERGE TO TRUNK. git-svn-id: svn://svn.icculus.org/gtkradiant/GtkRadiant/branches/Rambetter-temp-fixes@356 8a3a26a2-13c4-0310-b231-cf6edde360e5
1613 lines
44 KiB
C++
1613 lines
44 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;
|
|
}
|
|
}
|