]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/gtkmisc.cpp
Merge pull request #27 from freemancw/master
[xonotic/netradiant.git] / radiant / gtkmisc.cpp
1 /*
2    Copyright (c) 2001, Loki software, inc.
3    All rights reserved.
4
5    Redistribution and use in source and binary forms, with or without modification,
6    are permitted provided that the following conditions are met:
7
8    Redistributions of source code must retain the above copyright notice, this list
9    of conditions and the following disclaimer.
10
11    Redistributions in binary form must reproduce the above copyright notice, this
12    list of conditions and the following disclaimer in the documentation and/or
13    other materials provided with the distribution.
14
15    Neither the name of Loki software nor the names of its contributors may be used
16    to endorse or promote products derived from this software without specific prior
17    written permission.
18
19    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
20    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22    DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
23    DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26    ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 //
32 // Small functions to help with GTK
33 //
34
35 #include <gdk/gdkkeysyms.h>
36 #include <glib/gi18n.h>
37
38 #if defined ( __linux__ ) || defined ( __APPLE__ )
39 #include <unistd.h>
40 #endif
41
42 #include <gtk/gtk.h>
43
44 #ifdef _WIN32
45 #include <gdk/gdkwin32.h>
46 #define WIN32_LEAN_AND_MEAN
47 #include <windows.h>
48 #endif
49
50
51
52 #ifdef _WIN32
53 #include <io.h>
54 #include <direct.h>
55 #define R_OK 04
56 #endif
57 #include "stdafx.h"
58
59 // =============================================================================
60 // Misc stuff
61
62 // NOTE TTimo window position saving has always been tricky
63 //   it doesn't work the same between win32 and linux .. see below that code is fairly different
64 //   it's also very poorly done, the save calls are a bit randomly disctributed in the OnDestroy
65
66 void save_window_pos( GtkWidget *wnd, window_position_t& pos ){
67         if ( ( wnd == NULL ) || ( wnd->window == NULL ) ) {
68                 return;
69         }
70
71         get_window_pos( wnd, &pos.x, &pos.y );
72
73         pos.w = wnd->allocation.width;
74         pos.h = wnd->allocation.height;
75
76 #ifdef DBG_WINDOWPOS
77         //Sys_Printf("save_window_pos 'Window %s'\n",buf);
78 #endif
79 }
80
81 #ifdef _WIN32
82 void win32_get_window_pos( GtkWidget *widget, gint *x, gint *y ){
83         if ( g_PrefsDlg.m_bStartOnPrimMon ) {
84                 RECT rc;
85                 POINT point;
86                 HWND xwnd = (HWND)GDK_WINDOW_HWND( widget->window );
87                 const GdkRectangle primaryMonitorRect = g_pParentWnd->GetPrimaryMonitorRect();
88
89                 GetClientRect( xwnd,&rc );
90                 point.x = rc.left;
91                 point.y = rc.top;
92                 ClientToScreen( xwnd,&point );
93
94                 *x = point.x;
95                 *y = point.y;
96
97                 *x = max( *x,-widget->allocation.width + 10 );
98                 *x = min( *x,primaryMonitorRect.width - 10 );
99                 *y = max( *y,-widget->allocation.height + 10 );
100                 *y = min( *y,primaryMonitorRect.height - 10 );
101         }
102         else {
103                 // this is the same as the unix version of get_window_pos
104                 gdk_window_get_root_origin( widget->window, x, y );
105         }
106 #ifdef DBG_WINDOWPOS
107         Sys_Printf( "win32_get_window_pos %p %d,%d\n",widget,*x,*y );
108 #endif
109 }
110 #endif
111
112 void load_window_pos( GtkWidget *wnd, window_position_t& pos ){
113 #ifdef _WIN32
114         const GdkRectangle primaryMonitorRect = g_pParentWnd->GetPrimaryMonitorRect();
115
116         if ( pos.x < primaryMonitorRect.x
117                  || pos.y < primaryMonitorRect.y
118                  || pos.x > primaryMonitorRect.x + primaryMonitorRect.width
119                  || pos.y > primaryMonitorRect.y + primaryMonitorRect.height ) {
120                 gtk_window_set_position( GTK_WINDOW( wnd ), GTK_WIN_POS_CENTER_ON_PARENT );
121         }
122 #else
123         // FIXME: not multihead safe
124         if ( pos.x < 0
125                  || pos.y < 0
126                  || pos.x > gdk_screen_width()
127                  || pos.y > gdk_screen_height() ) {
128                 gtk_window_set_position( GTK_WINDOW( wnd ), GTK_WIN_POS_CENTER_ON_PARENT );
129         }
130 #endif
131         else{
132                 gtk_window_move( GTK_WINDOW( wnd ), pos.x, pos.y );
133         }
134
135         gtk_window_set_default_size( GTK_WINDOW( wnd ), pos.w, pos.h );
136 #ifdef DBG_WINDOWPOS
137         Sys_Printf( "load_window_pos %p 'Window,%s'\n",wnd,windowData );
138 #endif
139 }
140
141 gint widget_delete_hide( GtkWidget *widget ){
142         gtk_widget_hide( widget );
143
144         return TRUE;
145 }
146
147
148 // Thanks to Mercury, Fingolfin - ETG
149 int readLongLE( FILE *file, unsigned long *m_bytesRead, int *value ){
150         byte buf[4];
151         int len = fread( buf, 4, 1, file );
152         *m_bytesRead += 4;
153         if ( len != 1 ) {
154                 return -1;
155         }
156
157         *value = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
158         return 0;
159 }
160
161 short readShortLE( FILE *file, unsigned long *m_bytesRead, short unsigned *value ){
162         byte buf[2];
163         int len = fread( buf, 2, 1, file );
164         *m_bytesRead += 2;
165         if ( len != 1 ) {
166                 return -1;
167         }
168
169         *value = buf[0] | buf[1] << 8;
170         return 0;
171 }
172
173 unsigned char *load_bitmap_file( const char* filename, guint16 *width, guint16 *height ){
174         int bmWidth, bmHeight;
175         short unsigned bmPlanes, bmBitsPixel;
176         typedef struct {
177                 unsigned char rgbBlue;
178                 unsigned char rgbGreen;
179                 unsigned char rgbRed;
180                 unsigned char rgbReserved;
181         } RGBQUAD;
182         unsigned char m1,m2;
183         int sizeimage;
184         short unsigned res1,res2;
185         int filesize, pixoff;
186         int bmisize, compression;
187         int xscale, yscale;
188         int colors, impcol;
189         unsigned long m_bytesRead = 0;
190         unsigned char *imagebits = NULL;
191         FILE *fp;
192
193         *width = *height = 0;
194
195         fp = fopen( filename,"rb" );
196         if ( fp == NULL ) {
197                 return NULL;
198         }
199
200         size_t rc;
201         rc = fread( &m1, 1, 1, fp );
202         m_bytesRead++;
203         if ( rc == -1 ) {
204                 fclose( fp );
205                 return NULL;
206         }
207
208         rc = fread( &m2, 1, 1, fp );
209         m_bytesRead++;
210         if ( ( m1 != 'B' ) || ( m2 != 'M' ) ) {
211                 fclose( fp );
212                 return NULL;
213         }
214
215         if ( readLongLE( fp,&m_bytesRead,&filesize ) ) {
216                 fclose( fp );
217                 return NULL;
218         }
219
220         if ( readShortLE( fp,&m_bytesRead,&res1 ) ) {
221                 fclose( fp );
222                 return NULL;
223         }
224
225         if ( readShortLE( fp,&m_bytesRead,&res2 ) ) {
226                 fclose( fp );
227                 return NULL;
228         }
229
230         if ( readLongLE( fp,&m_bytesRead,&pixoff ) ) {
231                 fclose( fp );
232                 return NULL;
233         }
234
235         if ( readLongLE( fp,&m_bytesRead,&bmisize ) ) {
236                 fclose( fp );
237                 return NULL;
238         }
239
240         if ( readLongLE( fp,&m_bytesRead,&bmWidth ) ) {
241                 fclose( fp );
242                 return NULL;
243         }
244
245         if ( readLongLE( fp,&m_bytesRead,&bmHeight ) ) {
246                 fclose( fp );
247                 return NULL;
248         }
249
250         if ( readShortLE( fp,&m_bytesRead,&bmPlanes ) ) {
251                 fclose( fp );
252                 return NULL;
253         }
254
255         if ( readShortLE( fp,&m_bytesRead,&bmBitsPixel ) ) {
256                 fclose( fp );
257                 return NULL;
258         }
259
260         if ( readLongLE( fp,&m_bytesRead,&compression ) ) {
261                 fclose( fp );
262                 return NULL;
263         }
264
265         if ( readLongLE( fp,&m_bytesRead,&sizeimage ) ) {
266                 fclose( fp );
267                 return NULL;
268         }
269
270         if ( readLongLE( fp,&m_bytesRead,&xscale ) ) {
271                 fclose( fp );
272                 return NULL;
273         }
274
275         if ( readLongLE( fp,&m_bytesRead,&yscale ) ) {
276                 fclose( fp );
277                 return NULL;
278         }
279
280         if ( readLongLE( fp,&m_bytesRead,&colors ) ) {
281                 fclose( fp );
282                 return NULL;
283         }
284
285         if ( readLongLE( fp,&m_bytesRead,&impcol ) ) {
286                 fclose( fp );
287                 return NULL;
288         }
289
290         if ( colors == 0 ) {
291                 colors = 1 << bmBitsPixel;
292         }
293
294         RGBQUAD *colormap = NULL;
295         if ( bmBitsPixel != 24 ) {
296                 colormap = new RGBQUAD[colors];
297                 if ( colormap == NULL ) {
298                         fclose( fp );
299                         return NULL;
300                 }
301
302                 int i;
303                 for ( i = 0; i < colors; i++ )
304                 {
305                         unsigned char r,g, b, dummy;
306
307                         rc = fread( &b, 1, 1, fp );
308                         m_bytesRead++;
309                         if ( rc != 1 ) {
310                                 delete [] colormap;
311                                 fclose( fp );
312                                 return NULL;
313                         }
314
315                         rc = fread( &g, 1, 1, fp );
316                         m_bytesRead++;
317                         if ( rc != 1 ) {
318                                 delete [] colormap;
319                                 fclose( fp );
320                                 return NULL;
321                         }
322
323                         rc = fread( &r, 1, 1, fp );
324                         m_bytesRead++;
325                         if ( rc != 1 ) {
326                                 delete [] colormap;
327                                 fclose( fp );
328                                 return NULL;
329                         }
330
331                         rc = fread( &dummy, 1, 1, fp );
332                         m_bytesRead++;
333                         if ( rc != 1 ) {
334                                 delete [] colormap;
335                                 fclose( fp );
336                                 return NULL;
337                         }
338
339                         colormap[i].rgbRed = r;
340                         colormap[i].rgbGreen = g;
341                         colormap[i].rgbBlue = b;
342                 }
343         }
344
345         if ( (long)m_bytesRead > pixoff ) {
346                 delete [] colormap;
347                 fclose( fp );
348                 return NULL;
349         }
350
351         while ( (long)m_bytesRead < pixoff )
352         {
353                 char dummy;
354                 fread( &dummy,1,1,fp );
355                 m_bytesRead++;
356         }
357
358         int w = bmWidth;
359         int h = bmHeight;
360
361         // set the output params
362         imagebits = (unsigned char *)malloc( w * h * 3 );
363         long row_size = w * 3;
364
365         if ( imagebits != NULL ) {
366                 *width = w;
367                 *height = h;
368                 unsigned char *outbuf = imagebits;
369                 long row = 0;
370                 long rowOffset = 0;
371
372                 if ( compression == 0 ) { // BI_RGB
373                         // read rows in reverse order
374                         for ( row = bmHeight - 1; row >= 0; row-- )
375                         {
376                                 // which row are we working on?
377                                 rowOffset = (long unsigned)row * row_size;
378
379                                 if ( bmBitsPixel == 24 ) {
380                                         for ( int col = 0; col < w; col++ )
381                                         {
382                                                 long offset = col * 3;
383                                                 char pixel[3];
384
385                                                 if ( fread( (void *)( pixel ),1,3,fp ) == 3 ) {
386                                                         // we swap red and blue here
387                                                         *( outbuf + rowOffset + offset + 0 ) = pixel[2]; // r
388                                                         *( outbuf + rowOffset + offset + 1 ) = pixel[1]; // g
389                                                         *( outbuf + rowOffset + offset + 2 ) = pixel[0]; // b
390                                                 }
391                                         }
392                                         m_bytesRead += row_size;
393
394                                         // read DWORD padding
395                                         while ( ( m_bytesRead - pixoff ) & 3 )
396                                         {
397                                                 char dummy;
398                                                 if ( fread( &dummy,1,1,fp ) != 1 ) {
399                                                         free( imagebits );
400                                                         fclose( fp );
401                                                         return NULL;
402                                                 }
403                                                 m_bytesRead++;
404                                         }
405                                 }
406                                 else
407                                 {
408                                         // pixels are packed as 1 , 4 or 8 bit vals. need to unpack them
409                                         int bit_count = 0;
410                                         unsigned long mask = ( 1 << bmBitsPixel ) - 1;
411                                         unsigned char inbyte = 0;
412
413                                         for ( int col = 0; col < w; col++ )
414                                         {
415                                                 int pix = 0;
416
417                                                 // if we need another byte
418                                                 if ( bit_count <= 0 ) {
419                                                         bit_count = 8;
420                                                         if ( fread( &inbyte,1,1,fp ) != 1 ) {
421                                                                 free( imagebits );
422                                                                 delete [] colormap;
423                                                                 fclose( fp );
424                                                                 return NULL;
425                                                         }
426                                                         m_bytesRead++;
427                                                 }
428
429                                                 // keep track of where we are in the bytes
430                                                 bit_count -= bmBitsPixel;
431                                                 pix = ( inbyte >> bit_count ) & mask;
432
433                                                 // lookup the color from the colormap - stuff it in our buffer
434                                                 // swap red and blue
435                                                 *( outbuf + rowOffset + col * 3 + 2 ) = colormap[pix].rgbBlue;
436                                                 *( outbuf + rowOffset + col * 3 + 1 ) = colormap[pix].rgbGreen;
437                                                 *( outbuf + rowOffset + col * 3 + 0 ) = colormap[pix].rgbRed;
438                                         }
439
440                                         // read DWORD padding
441                                         while ( ( m_bytesRead - pixoff ) & 3 )
442                                         {
443                                                 char dummy;
444                                                 if ( fread( &dummy,1,1,fp ) != 1 ) {
445                                                         free( imagebits );
446                                                         if ( colormap ) {
447                                                                 delete [] colormap;
448                                                         }
449                                                         fclose( fp );
450                                                         return NULL;
451                                                 }
452                                                 m_bytesRead++;
453                                         }
454                                 }
455                         }
456                 }
457                 else
458                 {
459                         int i, x = 0;
460                         unsigned char c, c1 = 0, *pp;
461                         row = 0;
462                         pp = outbuf + ( bmHeight - 1 ) * bmWidth * 3;
463
464                         if ( bmBitsPixel == 8 ) {
465                                 while ( row < bmHeight )
466                                 {
467                                         c = getc( fp );
468
469                                         if ( c ) {
470                                                 // encoded mode
471                                                 c1 = getc( fp );
472                                                 for ( i = 0; i < c; x++, i++ )
473                                                 {
474                                                         *pp = colormap[c1].rgbRed; pp++;
475                                                         *pp = colormap[c1].rgbGreen; pp++;
476                                                         *pp = colormap[c1].rgbBlue; pp++;
477                                                 }
478                                         }
479                                         else
480                                         {
481                                                 // c==0x00,  escape codes
482                                                 c = getc( fp );
483                                                 if ( c == 0x00 ) { // end of line
484                                                         row++;
485                                                         x = 0;
486                                                         pp = outbuf + ( bmHeight - row - 1 ) * bmWidth * 3;
487                                                 }
488                                                 else if ( c == 0x01 ) {
489                                                         break; // end of pic
490                                                 }
491                                                 else if ( c == 0x02 ) { // delta
492                                                         c = getc( fp );
493                                                         x += c;
494                                                         c = getc( fp );
495                                                         row += c;
496                                                         pp = outbuf + x * 3 + ( bmHeight - row - 1 ) * bmWidth * 3;
497                                                 }
498                                                 else // absolute mode
499                                                 {
500                                                         for ( i = 0; i < c; x++, i++ )
501                                                         {
502                                                                 c1 = getc( fp );
503                                                                 *pp = colormap[c1].rgbRed; pp++;
504                                                                 *pp = colormap[c1].rgbGreen; pp++;
505                                                                 *pp = colormap[c1].rgbBlue; pp++;
506                                                         }
507
508                                                         if ( c & 1 ) {
509                                                                 getc( fp ); // odd length run: read an extra pad byte
510                                                         }
511                                                 }
512                                         }
513                                 }
514                         }
515                         else if ( bmBitsPixel == 4 ) {
516                                 while ( row < bmHeight )
517                                 {
518                                         c = getc( fp );
519
520                                         if ( c ) {
521                                                 // encoded mode
522                                                 c1 = getc( fp );
523                                                 for ( i = 0; i < c; x++, i++ )
524                                                 {
525                                                         *pp = colormap[( i & 1 ) ? ( c1 & 0x0f ) : ( ( c1 >> 4 ) & 0x0f )].rgbRed; pp++;
526                                                         *pp = colormap[( i & 1 ) ? ( c1 & 0x0f ) : ( ( c1 >> 4 ) & 0x0f )].rgbGreen; pp++;
527                                                         *pp = colormap[( i & 1 ) ? ( c1 & 0x0f ) : ( ( c1 >> 4 ) & 0x0f )].rgbBlue; pp++;
528                                                 }
529                                         }
530                                         else
531                                         {
532                                                 // c==0x00,  escape codes
533                                                 c = getc( fp );
534
535                                                 if ( c == 0x00 ) { // end of line
536                                                         row++;
537                                                         x = 0;
538                                                         pp = outbuf + ( bmHeight - row - 1 ) * bmWidth * 3;
539                                                 }
540                                                 else if ( c == 0x01 ) {
541                                                         break; // end of pic
542                                                 }
543                                                 else if ( c == 0x02 ) { // delta
544                                                         c = getc( fp );
545                                                         x += c;
546                                                         c = getc( fp );
547                                                         row += c;
548                                                         pp = outbuf + x * 3 + ( bmHeight - row - 1 ) * bmWidth * 3;
549                                                 }
550                                                 else // absolute mode
551                                                 {
552                                                         for ( i = 0; i < c; x++, i++ )
553                                                         {
554                                                                 if ( ( i & 1 ) == 0 ) {
555                                                                         c1 = getc( fp );
556                                                                 }
557                                                                 *pp = colormap[( i & 1 ) ? ( c1 & 0x0f ) : ( ( c1 >> 4 ) & 0x0f )].rgbRed; pp++;
558                                                                 *pp = colormap[( i & 1 ) ? ( c1 & 0x0f ) : ( ( c1 >> 4 ) & 0x0f )].rgbGreen; pp++;
559                                                                 *pp = colormap[( i & 1 ) ? ( c1 & 0x0f ) : ( ( c1 >> 4 ) & 0x0f )].rgbBlue; pp++;
560                                                         }
561
562                                                         if ( ( ( c & 3 ) == 1 ) || ( ( c & 3 ) == 2 ) ) {
563                                                                 getc( fp ); // odd length run: read an extra pad byte
564                                                         }
565                                                 }
566                                         }
567                                 }
568                         }
569                 }
570                 if ( colormap ) {
571                         delete [] colormap;
572                 }
573
574                 fclose( fp );
575         }
576         return imagebits;
577 }
578
579 void bmp_to_pixmap( const char* filename, GdkPixmap **pixmap, GdkBitmap **mask ){
580         guint16 width, height;
581         unsigned char *buf;
582         GdkWindow *window = gdk_get_default_root_window();
583         GdkColormap *colormap;
584         GdkGC* gc = gdk_gc_new( window );
585         int i, j;
586         bool hasMask = false;
587
588         *pixmap = *mask = NULL;
589         buf = load_bitmap_file( filename, &width, &height );
590         if ( !buf ) {
591                 return;
592         }
593
594         colormap = gdk_drawable_get_colormap( window );
595         *pixmap = gdk_pixmap_new( window, width, height, -1 );
596
597         typedef struct
598         {
599                 GdkColor c;
600                 unsigned char *p;
601         } PAL;
602
603         for ( i = 0; i < height; i++ )
604         {
605                 for ( j = 0; j < width; j++ )
606                 {
607                         unsigned char *p = &buf[( i * width + j ) * 3];
608                         PAL pe;
609
610                         pe.c.red = (gushort)( p[0] * 0xFF );
611                         pe.c.green = (gushort)( p[1] * 0xFF );
612                         pe.c.blue = (gushort)( p[2] * 0xFF );
613                         gdk_colormap_alloc_color( colormap, &pe.c, FALSE, TRUE );
614                         gdk_gc_set_foreground( gc, &pe.c );
615                         gdk_draw_point( *pixmap, gc, j, i );
616
617                         if ( p[0] == 0xFF && p[1] == 0x00 && p[2] == 0xFF ) {
618                                 hasMask = true;
619                         }
620                 }
621         }
622
623         gdk_gc_unref( gc );
624         *mask = gdk_pixmap_new( window, width, height, 1 );
625         gc = gdk_gc_new( *mask );
626         if ( hasMask ) {
627                 for ( i = 0; i < height; i++ )
628                 {
629                         for ( j = 0; j < width; j++ )
630                         {
631                                 GdkColor mask_pattern;
632
633                                 // pink is transparent
634                                 if ( ( buf[( i * width + j ) * 3] == 0xff ) &&
635                                          ( buf[( i * width + j ) * 3 + 1] == 0x00 ) &&
636                                          ( buf[( i * width + j ) * 3 + 2] == 0xff ) ) {
637                                         mask_pattern.pixel = 0;
638                                 }
639                                 else{
640                                         mask_pattern.pixel = 1;
641                                 }
642
643                                 gdk_gc_set_foreground( gc, &mask_pattern );
644                                 // possible Win32 Gtk bug here
645                                 //gdk_draw_point (*mask, gc, j, i);
646                                 gdk_draw_line( *mask, gc, j, i, j + 1, i );
647                         }
648                 }
649         }
650         else
651         {
652                 GdkColor mask_pattern;
653                 mask_pattern.pixel = 1;
654                 gdk_gc_set_foreground( gc, &mask_pattern );
655                 gdk_draw_rectangle( *mask, gc, 1, 0, 0, width, height );
656         }
657         gdk_gc_unref( gc );
658         free( buf );
659 }
660
661 void load_pixmap( const char* filename, GtkWidget* widget, GdkPixmap **gdkpixmap, GdkBitmap **mask ){
662         CString str;
663
664         str = g_strBitmapsPath;
665         str += filename;
666
667         bmp_to_pixmap( str.GetBuffer(), gdkpixmap, mask );
668         if ( *gdkpixmap == NULL ) {
669                 printf( "gdkpixmap was null\n" );
670                 gchar *dummy[] = { "1 1 1 1", "  c None", " " };
671                 printf( "calling gdk_pixmap_create_from_xpm_d\n" );
672                 *gdkpixmap = gdk_pixmap_create_from_xpm_d( gdk_get_default_root_window(), mask, NULL, dummy );
673         }
674 }
675
676 // this is the same as above but used by the plugins
677 // GdkPixmap **gdkpixmap, GdkBitmap **mask
678 bool WINAPI load_plugin_bitmap( const char* filename, void **gdkpixmap, void **mask ){
679         CString str;
680
681         str = g_strGameToolsPath;
682         str += g_strPluginsDir;
683         str += "bitmaps/";
684         str += filename;
685         bmp_to_pixmap( str.GetBuffer(), (GdkPixmap **)gdkpixmap, (GdkBitmap **)mask );
686
687         if ( *gdkpixmap == NULL ) {
688                 // look in the core plugins
689                 str = g_strAppPath;
690                 str += g_strPluginsDir;
691                 str += "bitmaps/";
692                 str += filename;
693                 bmp_to_pixmap( str.GetBuffer(), (GdkPixmap **)gdkpixmap, (GdkBitmap **)mask );
694
695                 if ( *gdkpixmap == NULL ) {
696
697                         // look in core modules
698                         str = g_strAppPath;
699                         str += g_strModulesDir;
700                         str += "bitmaps/";
701                         str += filename;
702                         bmp_to_pixmap( str.GetBuffer(), (GdkPixmap **)gdkpixmap, (GdkBitmap **)mask );
703
704                         if ( *gdkpixmap == NULL ) {
705                                 gchar *dummy[] = { "1 1 1 1", "  c None", " " };
706                                 *gdkpixmap = gdk_pixmap_create_from_xpm_d( gdk_get_default_root_window(), (GdkBitmap **)mask, NULL, dummy );
707                                 return false;
708                         }
709                 }
710         }
711         return true;
712 }
713
714 // Load a xpm file and return a pixmap widget.
715 GtkWidget* new_pixmap( GtkWidget* widget, const char* filename ){
716         GdkPixmap *gdkpixmap;
717         GdkBitmap *mask;
718         GtkWidget *pixmap;
719
720         load_pixmap( filename, widget, &gdkpixmap, &mask );
721         pixmap = gtk_pixmap_new( gdkpixmap, mask );
722
723         gdk_drawable_unref( gdkpixmap );
724         gdk_drawable_unref( mask );
725
726         return pixmap;
727 }
728
729 // =============================================================================
730 // Menu stuff
731
732 GtkWidget* menu_separator( GtkWidget *menu ){
733         GtkWidget *menu_item = gtk_menu_item_new();
734         gtk_menu_append( GTK_MENU( menu ), menu_item );
735         gtk_widget_set_sensitive( menu_item, FALSE );
736         gtk_widget_show( menu_item );
737         return menu_item;
738 }
739
740 GtkWidget* menu_tearoff( GtkWidget *menu ){
741         GtkWidget *menu_item = gtk_tearoff_menu_item_new();
742         gtk_menu_append( GTK_MENU( menu ), menu_item );
743 // gtk_widget_set_sensitive (menu_item, FALSE); -- controls whether menu is detachable
744         gtk_widget_show( menu_item );
745         return menu_item;
746 }
747
748 GtkWidget* create_sub_menu_with_mnemonic( GtkWidget *bar, const gchar *mnemonic ){
749         GtkWidget *item, *sub_menu;
750
751         item = gtk_menu_item_new_with_mnemonic( mnemonic );
752         gtk_widget_show( item );
753         gtk_container_add( GTK_CONTAINER( bar ), item );
754
755         sub_menu = gtk_menu_new();
756         gtk_menu_item_set_submenu( GTK_MENU_ITEM( item ), sub_menu );
757
758         return sub_menu;
759 }
760
761 extern void AddMenuItem( GtkWidget* menu, unsigned int id );
762
763 GtkWidget* create_menu_item_with_mnemonic( GtkWidget *menu, const gchar *mnemonic, GtkSignalFunc func, int id ){
764         GtkWidget *item;
765
766         item = gtk_menu_item_new_with_mnemonic( mnemonic );
767
768         gtk_widget_show( item );
769         gtk_container_add( GTK_CONTAINER( menu ), item );
770         gtk_signal_connect( GTK_OBJECT( item ), "activate", GTK_SIGNAL_FUNC( func ), GINT_TO_POINTER( id ) );
771
772         AddMenuItem( item, id );
773         return item;
774 }
775
776 GtkWidget* create_check_menu_item_with_mnemonic( GtkWidget *menu, const gchar *mnemonic, GtkSignalFunc func, int id, gboolean active ){
777         GtkWidget *item;
778
779         item = gtk_check_menu_item_new_with_mnemonic( mnemonic );
780
781         gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( item ), active );
782         gtk_widget_show( item );
783         gtk_container_add( GTK_CONTAINER( menu ), item );
784         gtk_signal_connect( GTK_OBJECT( item ), "activate", GTK_SIGNAL_FUNC( func ), GINT_TO_POINTER( id ) );
785
786         AddMenuItem( item, id );
787         return item;
788 }
789
790 GtkWidget* create_radio_menu_item_with_mnemonic( GtkWidget *menu, GtkWidget *last, const gchar *mnemonic, GtkSignalFunc func, int id, gboolean state ){
791         GtkWidget *item;
792         GSList *group = (GSList*)NULL;
793
794         if ( last != NULL ) {
795                 group = gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( last ) );
796         }
797         item = gtk_radio_menu_item_new_with_mnemonic( group, mnemonic );
798         gtk_check_menu_item_set_state( GTK_CHECK_MENU_ITEM( item ), state );
799
800         gtk_widget_show( item );
801         gtk_container_add( GTK_CONTAINER( menu ), item );
802         gtk_signal_connect( GTK_OBJECT( item ), "activate", GTK_SIGNAL_FUNC( func ), GINT_TO_POINTER( id ) );
803
804         AddMenuItem( item, id );
805         return item;
806 }
807
808 GtkWidget* create_menu_in_menu_with_mnemonic( GtkWidget *menu, const gchar *mnemonic ){
809         GtkWidget *item, *submenu;
810
811         item = gtk_menu_item_new_with_mnemonic( mnemonic );
812         gtk_widget_show( item );
813         gtk_container_add( GTK_CONTAINER( menu ), item );
814
815         submenu = gtk_menu_new();
816         gtk_menu_item_set_submenu( GTK_MENU_ITEM( item ), submenu );
817
818         return submenu;
819 }
820
821 // =============================================================================
822 // Message Boxes
823
824 void dialog_button_callback( GtkWidget *widget, gpointer data ) {
825         GtkWidget *parent;
826         int *loop, *ret;
827
828         parent = gtk_widget_get_toplevel( widget );
829         loop = (int*)g_object_get_data( G_OBJECT( parent ), "loop" );
830         ret = (int*)g_object_get_data( G_OBJECT( parent ), "ret" );
831
832         *loop = 0;
833         *ret = GPOINTER_TO_INT( data );
834 }
835
836 gint dialog_delete_callback( GtkWidget *widget, GdkEvent* event, gpointer data ){
837         int *loop;
838
839         gtk_widget_hide( widget );
840         loop = (int*)g_object_get_data( G_OBJECT( widget ), "loop" );
841         *loop = 0;
842
843         return TRUE;
844 }
845
846 gint dialog_url_callback( GtkWidget *widget, GdkEvent* event, gpointer data ){
847         OpenURL( (const char *)g_object_get_data( G_OBJECT( widget ), "URL" ) );
848
849         return TRUE;
850 }
851
852 int WINAPI gtk_MessageBox( void *parent, const char* lpText, const char* lpCaption, guint32 uType, const char* URL ){
853         GtkWidget *window, *w, *vbox, *hbox;
854         GtkAccelGroup *accel;
855         int mode = ( uType & MB_TYPEMASK ), ret, loop = 1;
856
857         window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
858         gtk_signal_connect( GTK_OBJECT( window ), "delete_event",
859                                                 GTK_SIGNAL_FUNC( dialog_delete_callback ), NULL );
860         gtk_signal_connect( GTK_OBJECT( window ), "destroy",
861                                                 GTK_SIGNAL_FUNC( gtk_widget_destroy ), NULL );
862         gtk_window_set_title( GTK_WINDOW( window ), lpCaption );
863         gtk_container_border_width( GTK_CONTAINER( window ), 10 );
864         g_object_set_data( G_OBJECT( window ), "loop", &loop );
865         g_object_set_data( G_OBJECT( window ), "ret", &ret );
866         gtk_widget_realize( window );
867
868         gtk_window_set_policy( GTK_WINDOW( window ),FALSE,FALSE,TRUE );
869
870         if ( parent != NULL ) {
871                 gtk_window_set_transient_for( GTK_WINDOW( window ), GTK_WINDOW( parent ) );
872         }
873
874         accel = gtk_accel_group_new();
875         gtk_window_add_accel_group( GTK_WINDOW( window ), accel );
876
877         vbox = gtk_vbox_new( FALSE, 10 );
878         gtk_container_add( GTK_CONTAINER( window ), vbox );
879         gtk_widget_show( vbox );
880
881         w = gtk_label_new( lpText );
882         gtk_box_pack_start( GTK_BOX( vbox ), w, FALSE, FALSE, 2 );
883         gtk_label_set_justify( GTK_LABEL( w ), GTK_JUSTIFY_LEFT );
884         gtk_widget_show( w );
885
886         w = gtk_hseparator_new();
887         gtk_box_pack_start( GTK_BOX( vbox ), w, FALSE, FALSE, 2 );
888         gtk_widget_show( w );
889
890         hbox = gtk_hbox_new( FALSE, 10 );
891         gtk_box_pack_start( GTK_BOX( vbox ), hbox, FALSE, FALSE, 2 );
892         gtk_widget_show( hbox );
893
894         if ( mode == MB_OK ) {
895                 w = gtk_button_new_with_label( _( "Ok" ) );
896                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
897                 gtk_signal_connect( GTK_OBJECT( w ), "clicked",
898                                                         GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDOK ) );
899                 gtk_widget_add_accelerator( w, "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
900                 gtk_widget_add_accelerator( w, "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
901                 GTK_WIDGET_SET_FLAGS( w, GTK_CAN_DEFAULT );
902                 gtk_widget_grab_default( w );
903                 gtk_widget_show( w );
904                 ret = IDOK;
905         }
906         else if ( mode ==  MB_OKCANCEL ) {
907                 w = gtk_button_new_with_label( _( "Ok" ) );
908                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
909                 gtk_signal_connect( GTK_OBJECT( w ), "clicked",
910                                                         GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDOK ) );
911                 gtk_widget_add_accelerator( w, "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
912                 GTK_WIDGET_SET_FLAGS( w, GTK_CAN_DEFAULT );
913                 gtk_widget_grab_default( w );
914                 gtk_widget_show( w );
915
916                 w = gtk_button_new_with_label( _( "Cancel" ) );
917                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
918                 gtk_signal_connect( GTK_OBJECT( w ), "clicked",
919                                                         GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDCANCEL ) );
920                 gtk_widget_add_accelerator( w, "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
921                 gtk_widget_show( w );
922                 ret = IDCANCEL;
923         }
924         else if ( mode == MB_YESNOCANCEL ) {
925                 w = gtk_button_new_with_label( _( "Yes" ) );
926                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
927                 gtk_signal_connect( GTK_OBJECT( w ), "clicked",
928                                                         GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDYES ) );
929                 GTK_WIDGET_SET_FLAGS( w, GTK_CAN_DEFAULT );
930                 gtk_widget_grab_default( w );
931                 gtk_widget_show( w );
932
933                 w = gtk_button_new_with_label( _( "No" ) );
934                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
935                 gtk_signal_connect( GTK_OBJECT( w ), "clicked",
936                                                         GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDNO ) );
937                 gtk_widget_show( w );
938
939                 w = gtk_button_new_with_label( _( "Cancel" ) );
940                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
941                 gtk_signal_connect( GTK_OBJECT( w ), "clicked",
942                                                         GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDCANCEL ) );
943                 gtk_widget_show( w );
944                 ret = IDCANCEL;
945         }
946         else /* if (mode == MB_YESNO) */
947         {
948                 w = gtk_button_new_with_label( _( "Yes" ) );
949                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
950                 gtk_signal_connect( GTK_OBJECT( w ), "clicked",
951                                                         GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDYES ) );
952                 GTK_WIDGET_SET_FLAGS( w, GTK_CAN_DEFAULT );
953                 gtk_widget_grab_default( w );
954                 gtk_widget_show( w );
955
956                 w = gtk_button_new_with_label( _( "No" ) );
957                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
958                 gtk_signal_connect( GTK_OBJECT( w ), "clicked",
959                                                         GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDNO ) );
960                 gtk_widget_show( w );
961                 ret = IDNO;
962         }
963
964         if ( URL ) {
965                 w = gtk_button_new_with_label( _( "Go to URL" ) );
966                 gtk_box_pack_start( GTK_BOX( hbox ), w, TRUE, TRUE, 0 );
967                 gtk_signal_connect( GTK_OBJECT( w ), "clicked",
968                                                         GTK_SIGNAL_FUNC( dialog_url_callback ), NULL );
969                 g_object_set_data( G_OBJECT( w ), "URL", (void *)URL );
970                 GTK_WIDGET_SET_FLAGS( w, GTK_CAN_DEFAULT );
971                 gtk_widget_grab_default( w );
972                 gtk_widget_show( w );
973         }
974
975
976         gtk_widget_show( window );
977         gtk_grab_add( window );
978
979         while ( loop )
980                 gtk_main_iteration();
981
982         gtk_grab_remove( window );
983         gtk_widget_destroy( window );
984
985         return ret;
986 }
987
988 // =============================================================================
989 // File dialog
990
991 // fenris #3078 WHENHELLISFROZENOVER
992
993 static void file_sel_callback( GtkWidget *widget, gpointer data ){
994         GtkWidget *parent;
995         int *loop;
996         bool *success;
997
998         parent = gtk_widget_get_toplevel( widget );
999         loop = (int*)g_object_get_data( G_OBJECT( parent ), "loop" );
1000         success = (bool*)g_object_get_data( G_OBJECT( parent ), "success" );
1001
1002         if ( GPOINTER_TO_INT( data ) == IDOK ) {
1003                 *success = true;
1004         }
1005
1006         *loop = 0;
1007 }
1008
1009 #ifdef _WIN32
1010 #include <commdlg.h>
1011 static OPENFILENAME ofn;       /* common dialog box structure   */
1012 static char szDirName[MAX_PATH];    /* directory string              */
1013 static char szFile[MAX_PATH];       /* filename string               */
1014 static char szFileTitle[MAX_PATH];  /* file title string             */
1015 static int i, cbString;        /* integer count variables       */
1016 static HANDLE hf;              /* file handle                   */
1017 #else
1018 static char szFile[QER_MAX_NAMELEN];
1019 #endif
1020
1021 #define FILEDLG_CUSTOM_FILTER_LENGTH 64
1022 // to be used with the advanced file selector
1023
1024 class CFileType : public IFileTypeList
1025 {
1026 struct filetype_copy_t
1027 {
1028         void operator=( const filetype_t& other ){
1029                 m_name = other.name;
1030                 m_pattern = other.pattern;
1031         }
1032         string_t m_name;
1033         string_t m_pattern;
1034 };
1035 public:
1036 CFileType(){
1037         m_nTypes = 0;
1038         m_pTypes = NULL;
1039         m_strWin32Filters = NULL;
1040         m_pstrGTKMasks = NULL;
1041 }
1042
1043 virtual ~CFileType(){
1044         delete[] m_pTypes;
1045         DestroyWin32Filters();
1046         DestroyGTKMasks();
1047 }
1048
1049 void addType( filetype_t type ){
1050         filetype_copy_t* newTypes = new filetype_copy_t [m_nTypes + 1];
1051         if ( m_nTypes > 0 ) {
1052                 for ( int i = 0; i < m_nTypes; i++ )
1053                         newTypes[i] = m_pTypes[i];
1054                 delete[] m_pTypes;
1055         }
1056         m_pTypes = newTypes;
1057         m_pTypes[m_nTypes] = type;
1058         m_nTypes++;
1059         ConstructGTKMasks();
1060         ConstructWin32Filters();
1061 }
1062
1063 filetype_t GetTypeForWin32Filter( const char *filter ) const {
1064         for ( int i = 0; i < m_nTypes; i++ )
1065                 if ( strcmp( m_pTypes[i].m_pattern.c_str(), filter ) == 0 ) {
1066                         return filetype_t( m_pTypes[i].m_name.c_str(), m_pTypes[i].m_pattern.c_str() );
1067                 }
1068         return filetype_t();
1069 }
1070
1071 filetype_t GetTypeForGTKMask( const char *mask ) const {
1072         for ( int i = 0; i < m_nTypes; i++ )
1073                 if ( strcmp( m_pstrGTKMasks[i],mask ) == 0 ) {
1074                         return filetype_t( m_pTypes[i].m_name.c_str(), m_pTypes[i].m_pattern.c_str() );
1075                 }
1076         return filetype_t();
1077 }
1078
1079 int GetNumTypes(){
1080         return m_nTypes;
1081 }
1082
1083 filetype_t GetTypeForIndex( int index ) const // Zero-based index.
1084 {
1085         if ( index >= 0 && index < m_nTypes ) {
1086                 return filetype_t( m_pTypes[index].m_name.c_str(), m_pTypes[index].m_pattern.c_str() );
1087         }
1088         return filetype_t();
1089 }
1090
1091 char *m_strWin32Filters;
1092 char **m_pstrGTKMasks;
1093 private:
1094 int m_nTypes;
1095 filetype_copy_t *m_pTypes;
1096
1097 void DestroyWin32Filters(){
1098         delete[] m_strWin32Filters;
1099 }
1100
1101 void ConstructWin32Filters(){
1102         const char *r;
1103         char *w;
1104         int i;
1105         int len = 0;
1106         DestroyWin32Filters();
1107         for ( i = 0; i < m_nTypes; i++ )
1108                 len = len + strlen( m_pTypes[i].m_name.c_str() ) + strlen( m_pTypes[i].m_pattern.c_str() ) * 2 + 5;
1109         m_strWin32Filters = new char[len + 1]; // length + null char
1110         for ( i = 0, w = m_strWin32Filters; i < m_nTypes; i++ )
1111         {
1112                 for ( r = m_pTypes[i].m_name.c_str(); *r != '\0'; r++, w++ )
1113                         *w = *r;
1114                 *w++ = ' ';
1115                 *w++ = '(';
1116                 for ( r = m_pTypes[i].m_pattern.c_str(); *r != '\0'; r++, w++ )
1117                         *w = *r;
1118                 *w++ = ')';
1119                 *w++ = '\0';
1120                 for ( r = m_pTypes[i].m_pattern.c_str(); *r != '\0'; r++, w++ )
1121                         *w = ( *r == ',' ) ? ';' : *r;
1122                 *w++ = '\0';
1123         }
1124         m_strWin32Filters[len] = '\0';
1125 }
1126
1127 void DestroyGTKMasks(){
1128         if ( m_pstrGTKMasks != NULL ) {
1129                 for ( char **p = m_pstrGTKMasks; *p != NULL; p++ )
1130                         delete[] *p;
1131         }
1132         delete[] m_pstrGTKMasks;
1133 }
1134
1135 void ConstructGTKMasks(){
1136         const char *r;
1137         char *w;
1138         int i;
1139         int len = 0;
1140         DestroyGTKMasks();
1141         m_pstrGTKMasks = new char*[m_nTypes + 1];
1142         for ( i = 0; i < m_nTypes; i++ )
1143         {
1144                 len = strlen( m_pTypes[i].m_name.c_str() ) + strlen( m_pTypes[i].m_pattern.c_str() ) + 3;
1145                 m_pstrGTKMasks[i] = new char[len + 1]; // length + null char
1146                 w = m_pstrGTKMasks[i];
1147                 for ( r = m_pTypes[i].m_name.c_str(); *r != '\0'; r++, w++ )
1148                         *w = *r;
1149                 *w++ = ' ';
1150                 *w++ = '(';
1151                 for ( r = m_pTypes[i].m_pattern.c_str(); *r != '\0'; r++, w++ )
1152                         *w = *r;
1153                 *w++ = ')';
1154                 *w++ = '\0';
1155         }
1156         m_pstrGTKMasks[m_nTypes] = NULL;
1157 }
1158
1159 };
1160
1161 #ifdef _WIN32
1162
1163 typedef struct {
1164         gboolean open;
1165         OPENFILENAME *ofn;
1166         BOOL dlgRtnVal;
1167         bool done;
1168 } win32_native_file_dialog_comms_t;
1169
1170 DWORD WINAPI win32_native_file_dialog_thread_func( LPVOID lpParam ){
1171         win32_native_file_dialog_comms_t *fileDialogComms;
1172         fileDialogComms = (win32_native_file_dialog_comms_t *) lpParam;
1173         if ( fileDialogComms->open ) {
1174                 fileDialogComms->dlgRtnVal = GetOpenFileName( fileDialogComms->ofn );
1175         }
1176         else {
1177                 fileDialogComms->dlgRtnVal = GetSaveFileName( fileDialogComms->ofn );
1178         }
1179         fileDialogComms->done = true; // No need to synchronize around lock, one-way gate.
1180         return 0;
1181 }
1182
1183 #endif
1184
1185 /**
1186  * @param[in] baseSubDir should have a trailing slash if not @c NULL
1187  */
1188 const char* file_dialog( void *parent, gboolean open, const char* title, const char* path, const char* pattern, const char *baseSubDir ){
1189
1190 #ifdef _WIN32
1191         static bool in_file_dialog = false;
1192         HANDLE fileDialogThreadHandle;
1193         win32_native_file_dialog_comms_t fileDialogComms;
1194         bool dialogDone;
1195 #endif
1196
1197         // Gtk dialog
1198         GtkWidget* file_sel;
1199         char *new_path = NULL;
1200
1201         const char* r;
1202         char *v, *w;
1203         filetype_t type;
1204         CFileType typelist;
1205         if ( pattern != NULL ) {
1206                 GetFileTypeRegistry()->getTypeList( pattern, &typelist );
1207         }
1208
1209 #ifdef _WIN32
1210         if ( g_PrefsDlg.m_bNativeGUI ) {
1211                 // do that the native way
1212
1213                 if ( in_file_dialog ) {
1214                         return NULL;             // Avoid recursive entry.
1215                 }
1216                 in_file_dialog = true;
1217                 /* Set the members of the OPENFILENAME structure. */
1218                 // See http://msdn.microsoft.com/en-us/library/ms646839%28v=vs.85%29.aspx .
1219                 memset( &ofn, 0, sizeof( ofn ) );
1220                 ofn.lStructSize = sizeof( ofn );
1221                 ofn.hwndOwner = (HWND)GDK_WINDOW_HWND( g_pParentWnd->m_pWidget->window );
1222                 ofn.nFilterIndex = 1; // The index is 1-based, not 0-based.  This basically says,
1223                                       // "select the first filter as default".
1224                 if ( pattern ) {
1225                         ofn.lpstrFilter = typelist.m_strWin32Filters;
1226                 }
1227                 else
1228                 {
1229                         // TODO: Would be a bit cleaner if we could extract this string from
1230                         // GetFileTypeRegistry() instead of hardcoding it here.
1231                         ofn.lpstrFilter = "all files\0*.*\0"; // Second '\0' will be added to end of string.
1232                 }
1233                 szFile[0] = '\0';
1234                 ofn.lpstrFile = szFile;
1235                 ofn.nMaxFile = sizeof( szFile );
1236                 if ( path ) {
1237                         // szDirName: Radiant uses unix convention for paths internally
1238                         //   Win32 (of course) and Gtk (who would have thought) expect the '\\' convention
1239                         // copy path, replacing dir separators as appropriate
1240                         for ( r = path, w = szDirName; *r != '\0'; r++ )
1241                                 *w++ = ( *r == '/' ) ? '\\' : *r;
1242                         // terminate string
1243                         *w = '\0';
1244                         ofn.lpstrInitialDir = szDirName;
1245                 }
1246                 ofn.lpstrTitle = title;
1247                 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
1248
1249                 memset( &fileDialogComms, 0, sizeof( fileDialogComms ) );
1250                 fileDialogComms.open = open;
1251                 fileDialogComms.ofn = &ofn;
1252
1253                 fileDialogThreadHandle =
1254                         CreateThread( NULL, // lpThreadAttributes
1255                                                   0, // dwStackSize, default stack size
1256                                                   win32_native_file_dialog_thread_func, // lpStartAddress, funcion to call
1257                                                   &fileDialogComms, // lpParameter, argument to pass to function
1258                                                   0, // dwCreationFlags
1259                                                   NULL ); // lpThreadId
1260
1261                 dialogDone = false;
1262                 while ( 1 ) {
1263                         // Avoid blocking indefinitely.  Another thread will set fileDialogComms->done to true;
1264                         // we don't want to be in an indefinite blocked state when this happens.  We want to break
1265                         // out of here eventually.
1266                         while ( gtk_events_pending() ) {
1267                                 gtk_main_iteration();
1268                         }
1269                         if ( dialogDone ) {
1270                                 break;
1271                         }
1272                         if ( fileDialogComms.done ) {
1273                                 dialogDone = true;                 // One more loop of gtk_main_iteration() to get things in sync.
1274                         }
1275                         // Avoid tight infinte loop, add a small amount of sleep.
1276                         Sleep( 10 );
1277                 }
1278                 // Make absolutely sure that the thread is finished before we call CloseHandle().
1279                 WaitForSingleObject( fileDialogThreadHandle, INFINITE );
1280                 CloseHandle( fileDialogThreadHandle );
1281
1282                 in_file_dialog = false;
1283
1284                 if ( !fileDialogComms.dlgRtnVal ) {
1285                         return NULL; // Cancelled.
1286                 }
1287
1288                 if ( pattern != NULL ) {
1289                         type = typelist.GetTypeForIndex( ofn.nFilterIndex - 1 );
1290                 }
1291
1292         }
1293         else
1294         {
1295 #endif
1296         char buf[PATH_MAX];
1297         // do that the Gtk way
1298         if ( title == NULL ) {
1299                 title = open ? _( "Open File" ) : _( "Save File" );
1300         }
1301
1302         // we expect an actual path below, if the path is NULL we might crash
1303         if ( !path || path[0] == '\0' ) {
1304                 strcpy( buf, g_pGameDescription->mEnginePath.GetBuffer() );
1305                 strcat( buf, g_pGameDescription->mBaseGame.GetBuffer() );
1306                 strcat( buf, "/" );
1307                 if ( baseSubDir ) {
1308                         strcat( buf, baseSubDir );
1309                 }
1310                 path = buf;
1311         }
1312
1313         // alloc new path with extra char for dir separator
1314         new_path = new char[strlen( path ) + 1 + 1];
1315         // copy path, replacing dir separators as appropriate
1316         for ( r = path, w = new_path; *r != '\0'; r++ )
1317                 *w++ = ( *r == '/' ) ? G_DIR_SEPARATOR : *r;
1318         // add dir separator to end of path if required
1319         if ( *( w - 1 ) != G_DIR_SEPARATOR ) {
1320                 *w++ = G_DIR_SEPARATOR;
1321         }
1322         // terminate string
1323         *w = '\0';
1324
1325         file_sel = gtk_file_chooser_dialog_new( title,
1326                                                                                         GTK_WINDOW( parent ),
1327                                                                                         open ? GTK_FILE_CHOOSER_ACTION_OPEN : GTK_FILE_CHOOSER_ACTION_SAVE,
1328                                                                                         GTK_STOCK_CANCEL,
1329                                                                                         GTK_RESPONSE_CANCEL,
1330                                                                                         open ? GTK_STOCK_OPEN : GTK_STOCK_SAVE,
1331                                                                                         GTK_RESPONSE_ACCEPT,
1332                                                                                         NULL );
1333         gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( file_sel ), new_path );
1334         delete[] new_path;
1335
1336         // Setting the file chooser dialog to modal and centering it on the parent is done automatically.
1337
1338         if ( pattern != NULL ) {
1339                 for ( int i = 0; i < typelist.GetNumTypes(); i++ ) {
1340                         GtkFileFilter *filter = gtk_file_filter_new();
1341                         type = typelist.GetTypeForIndex( i );
1342                         // We can use type.name here, or m_pstrGTKMasks[i], which includes the actual pattern.
1343                         gtk_file_filter_set_name( filter, typelist.m_pstrGTKMasks[i] );
1344                         gtk_file_filter_add_pattern( filter, type.pattern );
1345                         // "Note that the chooser takes ownership of the filter, so
1346                         // you have to ref and sink it if you want to keep a reference."
1347                         // So I guess we won't need to garbage collect this.
1348                         gtk_file_chooser_add_filter( GTK_FILE_CHOOSER( file_sel ), filter );
1349                 }
1350         }
1351
1352         if ( gtk_dialog_run( GTK_DIALOG( file_sel ) ) == GTK_RESPONSE_ACCEPT ) {
1353                 strcpy( szFile, gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( file_sel ) ) );
1354         }
1355         else {
1356                 szFile[0] = '\0';
1357         }
1358
1359         if ( pattern != NULL ) {
1360                 GtkFileFilter *filter = gtk_file_chooser_get_filter( GTK_FILE_CHOOSER( file_sel ) );
1361                 if ( filter == NULL ) {
1362                         type = filetype_t();
1363                 }
1364                 else {
1365                         type = typelist.GetTypeForGTKMask( gtk_file_filter_get_name( filter ) );
1366                 }
1367         }
1368         gtk_widget_destroy( file_sel );
1369
1370 #ifdef _WIN32
1371 }
1372 #endif
1373
1374         // don't return an empty filename
1375         if ( szFile[0] == '\0' ) {
1376                 return NULL;
1377         }
1378
1379         // convert back to unix format
1380         for ( w = szFile; *w != '\0'; w++ )
1381                 if ( *w == '\\' ) {
1382                         *w = '/';
1383                 }
1384
1385         /* \todo SPoG - file_dialog should return filetype information separately.. not force file extension.. */
1386         if ( !open && pattern != NULL ) {
1387                 v = strrchr( szFile, '/' );
1388                 w = strrchr( szFile, '.' );
1389                 if ( ( v && w && w < v ) || // Last '.' is before the file.
1390                          w == NULL ) { // Extension missing.
1391                         if ( type.pattern[0] ) {
1392                                 w = szFile + strlen( szFile );
1393                                 strcpy( w, type.pattern + 1 ); // Add extension of selected filter type.
1394                         }
1395                         else {
1396                                 // type will be empty if for example there were no filters for pattern,
1397                                 // or if some other UI inconsistencies happen.
1398                                 if ( gtk_MessageBox( parent, "No file extension specified in file to be saved.\nAttempt to save anyways?",
1399                                                                          "GtkRadiant", MB_YESNO ) == IDNO ) {
1400                                         return NULL;
1401                                 }
1402                         }
1403                 }
1404                 else { // An extension was explicitly in the filename.
1405                         bool knownExtension = false;
1406                         for ( int i = typelist.GetNumTypes() - 1; i >= 0; i-- ) {
1407                                 type = typelist.GetTypeForIndex( i );
1408                                 if ( type.pattern[0] && strcmp( w, type.pattern + 1 ) == 0 ) {
1409                                         knownExtension = true;
1410                                         break;
1411                                 }
1412                         }
1413                         if ( !knownExtension ) {
1414                                 if ( gtk_MessageBox( parent, "Unknown file extension for this save operation.\nAttempt to save anyways?",
1415                                                                          "GtkRadiant", MB_YESNO ) == IDNO ) {
1416                                         return NULL;
1417                                 }
1418                         }
1419                 }
1420         }
1421
1422         // prompt to overwrite existing files
1423         if ( !open ) {
1424                 if ( access( szFile, R_OK ) == 0 ) {
1425                         if ( gtk_MessageBox( parent, "File already exists.\nOverwrite?", "GtkRadiant", MB_YESNO ) == IDNO ) {
1426                                 return NULL;
1427                         }
1428                 }
1429         }
1430
1431         return szFile;
1432 }
1433
1434 char* WINAPI dir_dialog( void *parent, const char* title, const char* path ){
1435         GtkWidget* file_sel;
1436         char* filename = (char*)NULL;
1437         int loop = 1;
1438         bool success = false;
1439
1440         file_sel = gtk_file_selection_new( title );
1441         gtk_signal_connect( GTK_OBJECT( GTK_FILE_SELECTION( file_sel )->ok_button ), "clicked",
1442                                                 GTK_SIGNAL_FUNC( file_sel_callback ), GINT_TO_POINTER( IDOK ) );
1443         gtk_signal_connect( GTK_OBJECT( GTK_FILE_SELECTION( file_sel )->cancel_button ), "clicked",
1444                                                 GTK_SIGNAL_FUNC( file_sel_callback ), GINT_TO_POINTER( IDCANCEL ) );
1445         gtk_signal_connect( GTK_OBJECT( file_sel ), "delete_event",
1446                                                 GTK_SIGNAL_FUNC( dialog_delete_callback ), NULL );
1447         gtk_file_selection_hide_fileop_buttons( GTK_FILE_SELECTION( file_sel ) );
1448
1449         if ( parent != NULL ) {
1450                 gtk_window_set_transient_for( GTK_WINDOW( file_sel ), GTK_WINDOW( parent ) );
1451         }
1452
1453         gtk_widget_hide( GTK_FILE_SELECTION( file_sel )->file_list->parent );
1454
1455         g_object_set_data( G_OBJECT( file_sel ), "loop", &loop );
1456         g_object_set_data( G_OBJECT( file_sel ), "success", &success );
1457
1458         if ( path != NULL ) {
1459                 gtk_file_selection_set_filename( GTK_FILE_SELECTION( file_sel ), path );
1460         }
1461
1462         gtk_grab_add( file_sel );
1463         gtk_widget_show( file_sel );
1464
1465         while ( loop )
1466                 gtk_main_iteration();
1467
1468         filename = g_strdup( gtk_file_selection_get_filename( GTK_FILE_SELECTION( file_sel ) ) );
1469
1470         gtk_grab_remove( file_sel );
1471         gtk_widget_destroy( file_sel );
1472
1473         return filename;
1474 }
1475
1476 bool WINAPI color_dialog( void *parent, float *color, const char* title ){
1477         GtkWidget* dlg;
1478         double clr[3];
1479         int loop = 1, ret = IDCANCEL;
1480
1481         clr[0] = color[0];
1482         clr[1] = color[1];
1483         clr[2] = color[2];
1484
1485         dlg = gtk_color_selection_dialog_new( title );
1486         gtk_color_selection_set_color( GTK_COLOR_SELECTION( GTK_COLOR_SELECTION_DIALOG( dlg )->colorsel ), clr );
1487         gtk_signal_connect( GTK_OBJECT( dlg ), "delete_event",
1488                                                 GTK_SIGNAL_FUNC( dialog_delete_callback ), NULL );
1489         gtk_signal_connect( GTK_OBJECT( dlg ), "destroy",
1490                                                 GTK_SIGNAL_FUNC( gtk_widget_destroy ), NULL );
1491         gtk_signal_connect( GTK_OBJECT( GTK_COLOR_SELECTION_DIALOG( dlg )->ok_button ), "clicked",
1492                                                 GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDOK ) );
1493         gtk_signal_connect( GTK_OBJECT( GTK_COLOR_SELECTION_DIALOG( dlg )->cancel_button ), "clicked",
1494                                                 GTK_SIGNAL_FUNC( dialog_button_callback ), GINT_TO_POINTER( IDCANCEL ) );
1495         g_object_set_data( G_OBJECT( dlg ), "loop", &loop );
1496         g_object_set_data( G_OBJECT( dlg ), "ret", &ret );
1497
1498         if ( parent != NULL ) {
1499                 gtk_window_set_transient_for( GTK_WINDOW( dlg ), GTK_WINDOW( parent ) );
1500         }
1501
1502         gtk_widget_show( dlg );
1503         gtk_grab_add( dlg );
1504
1505         while ( loop )
1506                 gtk_main_iteration();
1507
1508         GdkColor gdkcolor;
1509         gtk_color_selection_get_current_color( GTK_COLOR_SELECTION( GTK_COLOR_SELECTION_DIALOG( dlg )->colorsel ), &gdkcolor );
1510         clr[0] = gdkcolor.red / 65535.0;
1511         clr[1] = gdkcolor.green / 65535.0;
1512         clr[2] = gdkcolor.blue / 65535.0;
1513
1514         gtk_grab_remove( dlg );
1515         gtk_widget_destroy( dlg );
1516
1517         if ( ret == IDOK ) {
1518                 color[0] = (float)clr[0];
1519                 color[1] = (float)clr[1];
1520                 color[2] = (float)clr[2];
1521
1522                 return true;
1523         }
1524
1525         return false;
1526 }
1527
1528 void OpenURL( const char *url ){
1529         // let's put a little comment
1530         Sys_Printf( "OpenURL: %s\n", url );
1531 #ifdef __linux__
1532         // \todo FIXME: the way we open URLs on *nix should be improved. A script is good (see how I do on RTCW)
1533         char command[2 * PATH_MAX];
1534         snprintf( command, sizeof( command ), "%s/openurl.sh \"%s\" &", g_strAppPath.GetBuffer(), url );
1535         if ( system( command ) != 0 ) {
1536                 gtk_MessageBox( g_pParentWnd->m_pWidget, "Failed to launch Netscape!" );
1537         }
1538 #endif
1539 #ifdef __APPLE__
1540         char command[2 * PATH_MAX];
1541         snprintf( command, sizeof( command ),
1542                           "open \"%s\" &", url, url );
1543         if ( system( command ) != 0 ) {
1544                 gtk_MessageBox( g_pParentWnd->m_pWidget, "Unable to launch browser!" );
1545         }
1546 #endif
1547 #ifdef _WIN32
1548         ShellExecute( (HWND)GDK_WINDOW_HWND( g_pParentWnd->m_pWidget->window ), "open", url, NULL, NULL, SW_SHOW );
1549 #endif
1550 }
1551
1552 void CheckMenuSplitting( GtkWidget *&menu ){
1553         GtkWidget *item,*menu2;
1554
1555         GtkRequisition requisition;
1556         gint screen_height;
1557
1558         gtk_widget_size_request( GTK_WIDGET( menu ), &requisition );
1559         screen_height = gdk_screen_height();
1560
1561         if ( ( screen_height - requisition.height ) < 20 ) {
1562                 menu2 = gtk_menu_new();
1563
1564                 // move the last 2 items to a submenu (3 because of win32)
1565                 for ( int i = 0; i < 3; i++ )
1566                 {
1567                         item = GTK_WIDGET( g_list_last( gtk_container_children( GTK_CONTAINER( menu ) ) )->data );
1568                         gtk_widget_ref( item );
1569                         gtk_container_remove( GTK_CONTAINER( menu ), item );
1570                         gtk_menu_append( GTK_MENU( menu2 ), item );
1571                         gtk_widget_unref( item );
1572                 }
1573
1574                 item = gtk_menu_item_new_with_label( "--------" );
1575                 gtk_widget_show( item );
1576                 gtk_container_add( GTK_CONTAINER( menu ), item );
1577                 gtk_menu_item_set_submenu( GTK_MENU_ITEM( item ), menu2 );
1578                 menu = menu2;
1579         }
1580 }