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