more eol-style
[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
37 #if defined (__linux__) || defined (__APPLE__)
38 #include <unistd.h>
39 #endif
40
41 #include <gtk/gtk.h>
42
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     char *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         char *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, 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, 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, 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, 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, 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 {
856   GtkWidget *parent;
857   int *loop, *ret;
858
859   parent = gtk_widget_get_toplevel (widget);
860   loop = (int*)g_object_get_data (G_OBJECT (parent), "loop");
861   ret = (int*)g_object_get_data (G_OBJECT (parent), "ret");
862
863   *loop = 0;
864   *ret = (int)data;
865 }
866
867 gint dialog_delete_callback (GtkWidget *widget, GdkEvent* event, gpointer data)
868 {
869   int *loop;
870
871   gtk_widget_hide (widget);
872   loop = (int*)g_object_get_data (G_OBJECT (widget), "loop");
873   *loop = 0;
874
875   return TRUE;
876 }
877
878 gint dialog_url_callback (GtkWidget *widget, GdkEvent* event, gpointer data)
879 {
880   OpenURL((const char *)g_object_get_data (G_OBJECT (widget), "URL"));
881
882   return TRUE;
883 }
884
885 int WINAPI gtk_MessageBox (void *parent, const char* lpText, const char* lpCaption, guint32 uType, const char* URL)
886 {
887   GtkWidget *window, *w, *vbox, *hbox;
888   GtkAccelGroup *accel;
889   int mode = (uType & MB_TYPEMASK), ret, loop = 1;
890
891   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
892   gtk_signal_connect (GTK_OBJECT (window), "delete_event",
893                       GTK_SIGNAL_FUNC (dialog_delete_callback), NULL);
894   gtk_signal_connect (GTK_OBJECT (window), "destroy",
895                       GTK_SIGNAL_FUNC (gtk_widget_destroy), NULL);
896   gtk_window_set_title (GTK_WINDOW (window), lpCaption);
897   gtk_container_border_width (GTK_CONTAINER (window), 10);
898   g_object_set_data (G_OBJECT (window), "loop", &loop);
899   g_object_set_data (G_OBJECT (window), "ret", &ret);
900   gtk_widget_realize (window);
901
902   gtk_window_set_policy(GTK_WINDOW (window),FALSE,FALSE,TRUE);
903
904   if (parent != NULL)
905     gtk_window_set_transient_for (GTK_WINDOW (window), GTK_WINDOW (parent));
906
907   accel = gtk_accel_group_new ();
908   gtk_window_add_accel_group (GTK_WINDOW (window), accel);
909
910   vbox = gtk_vbox_new (FALSE, 10);
911   gtk_container_add (GTK_CONTAINER (window), vbox);
912   gtk_widget_show (vbox);
913
914   w = gtk_label_new (lpText);
915   gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 2);
916   gtk_label_set_justify (GTK_LABEL (w), GTK_JUSTIFY_LEFT);
917   gtk_widget_show (w);
918
919   w = gtk_hseparator_new ();
920   gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 2);
921   gtk_widget_show (w);
922
923   hbox = gtk_hbox_new (FALSE, 10);
924   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 2);
925   gtk_widget_show (hbox);
926  
927   if (mode == MB_OK)
928   {
929     w = gtk_button_new_with_label ("Ok");
930     gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
931     gtk_signal_connect (GTK_OBJECT (w), "clicked",
932                         GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDOK));
933     gtk_widget_add_accelerator (w, "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0);
934     gtk_widget_add_accelerator (w, "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0);
935     GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
936     gtk_widget_grab_default (w);
937     gtk_widget_show (w);
938     ret = IDOK;
939   }
940   else if (mode ==  MB_OKCANCEL)
941   {
942     w = gtk_button_new_with_label ("Ok");
943     gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
944     gtk_signal_connect (GTK_OBJECT (w), "clicked",
945                         GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDOK));
946     gtk_widget_add_accelerator (w, "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0);
947     GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
948     gtk_widget_grab_default (w);
949     gtk_widget_show (w);
950
951     w = gtk_button_new_with_label ("Cancel");
952     gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
953     gtk_signal_connect (GTK_OBJECT (w), "clicked",
954                         GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDCANCEL));
955     gtk_widget_add_accelerator (w, "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0);
956     gtk_widget_show (w);
957     ret = IDCANCEL;
958   }
959   else if (mode == MB_YESNOCANCEL)
960   {
961     w = gtk_button_new_with_label ("Yes");
962     gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
963     gtk_signal_connect (GTK_OBJECT (w), "clicked",
964                         GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDYES));
965     GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
966     gtk_widget_grab_default (w);
967     gtk_widget_show (w);
968  
969     w = gtk_button_new_with_label ("No");
970     gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
971     gtk_signal_connect (GTK_OBJECT (w), "clicked",
972                         GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDNO));
973     gtk_widget_show (w);
974  
975     w = gtk_button_new_with_label ("Cancel");
976     gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
977     gtk_signal_connect (GTK_OBJECT (w), "clicked",
978                         GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDCANCEL));
979     gtk_widget_show (w);
980     ret = IDCANCEL;
981   }
982   else /* if (mode == MB_YESNO) */
983   {
984     w = gtk_button_new_with_label ("Yes");
985     gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
986     gtk_signal_connect (GTK_OBJECT (w), "clicked",
987                         GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDYES));
988     GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
989     gtk_widget_grab_default (w);
990     gtk_widget_show (w);
991  
992     w = gtk_button_new_with_label ("No");
993     gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
994     gtk_signal_connect (GTK_OBJECT (w), "clicked",
995                         GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDNO));
996     gtk_widget_show (w);
997     ret = IDNO;
998   }
999
1000   if (URL)
1001   {
1002     w = gtk_button_new_with_label ("Go to URL");
1003     gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
1004     gtk_signal_connect (GTK_OBJECT (w), "clicked",
1005                         GTK_SIGNAL_FUNC (dialog_url_callback), NULL);
1006     g_object_set_data (G_OBJECT (w), "URL", (void *)URL);
1007     GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
1008     gtk_widget_grab_default (w);
1009     gtk_widget_show (w);
1010   }
1011
1012
1013   gtk_widget_show (window);
1014   gtk_grab_add (window);
1015
1016   while (loop)
1017     gtk_main_iteration ();
1018
1019   gtk_grab_remove (window);
1020   gtk_widget_destroy (window);
1021
1022   return ret;
1023 }
1024
1025 // =============================================================================
1026 // File dialog
1027
1028 // fenris #3078 WHENHELLISFROZENOVER
1029
1030 //#define FILEDLG_DBG
1031
1032 static void file_sel_callback (GtkWidget *widget, gpointer data)
1033 {
1034   GtkWidget *parent;
1035   int *loop;
1036   bool *success;
1037
1038   parent = gtk_widget_get_toplevel (widget);
1039   loop = (int*)g_object_get_data (G_OBJECT (parent), "loop");
1040   success = (bool*)g_object_get_data (G_OBJECT (parent), "success");
1041   
1042   if ((int)data == IDOK)
1043     *success = true;
1044
1045 #ifdef FILEDLG_DBG
1046   else
1047     Sys_Printf("file_sel_callback != IDOK\n");
1048 #endif
1049   
1050   *loop = 0;
1051 }
1052
1053 #ifdef _WIN32
1054 #include <commdlg.h>
1055 static OPENFILENAME ofn;       /* common dialog box structure   */ 
1056 static char szDirName[MAX_PATH];    /* directory string              */ 
1057 static char szFile[MAX_PATH];       /* filename string               */ 
1058 static char szFileTitle[MAX_PATH];  /* file title string             */ 
1059 static int i, cbString;        /* integer count variables       */ 
1060 static HANDLE hf;              /* file handle                   */ 
1061 #else
1062 static char szFile[QER_MAX_NAMELEN];
1063 #endif
1064
1065 #define FILEDLG_CUSTOM_FILTER_LENGTH 64
1066 // to be used with the advanced file selector
1067
1068 class CFileType : public IFileTypeList
1069 {
1070   struct filetype_copy_t
1071   {
1072     void operator=(const filetype_t& other)
1073     {
1074       m_name = other.name;
1075       m_pattern = other.pattern;
1076     }
1077     string_t m_name;
1078     string_t m_pattern;
1079   };
1080 public:
1081   CFileType()
1082   {
1083     m_nTypes = 0;
1084     m_pTypes = NULL;
1085     m_strWin32Filters = NULL;
1086     m_pstrGTKMasks = NULL;
1087   }
1088
1089   virtual ~CFileType()
1090   {
1091     delete[] m_pTypes;
1092     DestroyWin32Filters();
1093     DestroyGTKMasks();
1094   }
1095
1096   void addType(filetype_t type)
1097   {
1098     filetype_copy_t* newTypes = new filetype_copy_t [m_nTypes+1];
1099     if(m_nTypes > 0)
1100     {
1101       for(int i=0; i<m_nTypes; i++)
1102         newTypes[i] = m_pTypes[i];
1103       delete[] m_pTypes;
1104     }
1105     m_pTypes = newTypes;
1106     m_pTypes[m_nTypes] = type;
1107     m_nTypes++;
1108     ConstructGTKMasks();
1109     ConstructWin32Filters();
1110   }
1111  
1112   filetype_t GetTypeForWin32Filter(const char *filter) const
1113   {
1114     for(int i=0; i<m_nTypes; i++)
1115       if(strcmp(m_pTypes[i].m_pattern.c_str(), filter)==0)
1116         return filetype_t(m_pTypes[i].m_name.c_str(), m_pTypes[i].m_pattern.c_str());
1117     return filetype_t();
1118   }
1119
1120   filetype_t GetTypeForGTKMask(const char *mask) const
1121   {
1122     for(int i=0; i<m_nTypes; i++)
1123       if(strcmp(m_pstrGTKMasks[i],mask)==0)
1124         return filetype_t(m_pTypes[i].m_name.c_str(), m_pTypes[i].m_pattern.c_str());
1125     return filetype_t();
1126   }
1127
1128   char *m_strWin32Filters;
1129   char **m_pstrGTKMasks;
1130 private:
1131   int m_nTypes;
1132   filetype_copy_t *m_pTypes;
1133
1134   void DestroyWin32Filters()
1135   {
1136     delete[] m_strWin32Filters;
1137   }
1138
1139   void ConstructWin32Filters()
1140   {
1141     const char *r;
1142     char *w;
1143     int i;
1144     int len = 0;
1145     DestroyWin32Filters();
1146     for(i=0; i<m_nTypes; i++)
1147       len = len + strlen(m_pTypes[i].m_name.c_str()) + strlen(m_pTypes[i].m_pattern.c_str())*2 + 5;
1148     m_strWin32Filters = new char[len+1]; // length + null char
1149     for(i=0, w = m_strWin32Filters; i<m_nTypes; i++)
1150     {
1151       for(r = m_pTypes[i].m_name.c_str(); *r!='\0'; r++, w++)
1152         *w = *r;
1153       *w++ = ' ';
1154       *w++ = '(';
1155       for(r = m_pTypes[i].m_pattern.c_str(); *r!='\0'; r++, w++)
1156         *w = *r;
1157       *w++ = ')';
1158       *w++ = '\0';
1159       for(r = m_pTypes[i].m_pattern.c_str(); *r!='\0'; r++, w++)
1160         *w = (*r == ',') ? ';' : *r;
1161       *w++ = '\0';
1162     }
1163     m_strWin32Filters[len] = '\0';
1164   }
1165
1166   void DestroyGTKMasks()
1167   {
1168     if(m_pstrGTKMasks != NULL)
1169       for(char **p = m_pstrGTKMasks; *p != NULL; p++)
1170         delete[] *p;
1171     delete[] m_pstrGTKMasks;
1172   }
1173   
1174   void ConstructGTKMasks()
1175   {
1176     const char *r;
1177     char *w;
1178     int i;
1179     int len = 0;
1180     DestroyGTKMasks();
1181     m_pstrGTKMasks = new char*[m_nTypes+1];
1182     for(i=0; i<m_nTypes; i++)
1183     {
1184       len = strlen(m_pTypes[i].m_name.c_str()) + strlen(m_pTypes[i].m_pattern.c_str()) + 3;
1185       m_pstrGTKMasks[i] = new char[len+1]; // length + null char
1186       w = m_pstrGTKMasks[i];
1187       for(r = m_pTypes[i].m_name.c_str(); *r!='\0'; r++, w++)
1188         *w = *r;
1189       *w++ = ' ';
1190       *w++ = '<';
1191       for(r = m_pTypes[i].m_pattern.c_str(); *r!='\0'; r++, w++)
1192         *w = *r;
1193       *w++ = '>';
1194       *w++ = '\0';
1195     }
1196     m_pstrGTKMasks[m_nTypes] = NULL;
1197   }
1198
1199 };
1200
1201 const char* file_dialog (void *parent, gboolean open, const char* title, const char* path, const char* pattern)
1202 {
1203   // Gtk dialog
1204   GtkWidget* file_sel;
1205   int loop = 1;
1206   char *new_path = NULL;
1207
1208   const char* r;
1209   char* w;
1210   filetype_t type;
1211   CFileType typelist;
1212   if(pattern != NULL)
1213     GetFileTypeRegistry()->getTypeList(pattern, &typelist);
1214
1215 #ifdef FILEDLG_DBG
1216   Sys_Printf("file_dialog: open = %d title = %s path = %s\n", open, title, path);
1217   if (pattern)
1218   {
1219     Sys_Printf("Patterns:\n");
1220     char** p = typelist.m_pstrGTKMasks;
1221     while(*p!=NULL)
1222       Sys_Printf("%s\n", *p++);
1223   }
1224   else
1225     Sys_Printf("no patterns\n");
1226 #endif
1227
1228 #ifdef _WIN32
1229   // win32 dialog stores the selected "save as type" extension in the second null-terminated string
1230   char customfilter[FILEDLG_CUSTOM_FILTER_LENGTH];
1231
1232   if (g_PrefsDlg.m_bNativeGUI)
1233   {
1234 #ifdef FILEDLG_DBG
1235     Sys_Printf("Doing win32 file dialog...");
1236 #endif
1237     // do that the native way
1238     /* Place the terminating null character in the szFile. */  
1239     szFile[0] = '\0';
1240     customfilter[0] = customfilter[1] = customfilter[2] = '\0';
1241     
1242     /* Set the members of the OPENFILENAME structure. */     
1243     ofn.lStructSize = sizeof(OPENFILENAME); 
1244     ofn.hwndOwner = (HWND)GDK_WINDOW_HWND (g_pParentWnd->m_pWidget->window);
1245     if (pattern)
1246     {
1247       ofn.nFilterIndex = 0;
1248       ofn.lpstrFilter = typelist.m_strWin32Filters;
1249     }
1250     else ofn.nFilterIndex = 1;
1251     ofn.lpstrCustomFilter = customfilter;
1252     ofn.nMaxCustFilter = sizeof(customfilter);
1253     ofn.lpstrFile = szFile;
1254     ofn.nMaxFile = sizeof(szFile); 
1255     ofn.lpstrFileTitle = NULL; // we don't need to get the name of the file
1256     if(path)
1257     {
1258       // szDirName: Radiant uses unix convention for paths internally
1259       //   Win32 (of course) and Gtk (who would have thought) expect the '\\' convention
1260       // copy path, replacing dir separators as appropriate
1261       for(r=path, w=szDirName; *r!='\0'; r++)
1262         *w++ = (*r=='/') ? '\\' : *r;
1263       // terminate string
1264       *w = '\0';
1265       ofn.lpstrInitialDir = szDirName;
1266     }
1267     else ofn.lpstrInitialDir = NULL;
1268     ofn.lpstrTitle = title;
1269     ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; 
1270
1271     /* Display the Open dialog box. */
1272     // it's open or close depending on 'open' parameter
1273     if (open)
1274     {
1275       if (!GetOpenFileName(&ofn))
1276         return NULL;    // canceled
1277     }
1278     else
1279     {
1280       if (!GetSaveFileName(&ofn))
1281         return NULL;    // canceled
1282     }
1283
1284     if(pattern != NULL)
1285       type = typelist.GetTypeForWin32Filter(customfilter+1);
1286     
1287 #ifdef FILEDLG_DBG
1288     Sys_Printf("Done.\n");
1289 #endif
1290   }
1291   else
1292   {
1293 #endif
1294     // do that the Gtk way
1295     if (title == NULL)
1296       title = open ? "Open File" : "Save File";
1297     
1298 #ifdef FILEDLG_DBG
1299     Sys_Printf("Doing Gtk file dialog:\nBuilding new_path..");
1300 #endif
1301     // we expect an actual path below, if the path is NULL we might crash
1302     if (!path || path[0] == '\0')
1303     {
1304 #ifdef _WIN32
1305       path = "C:\\";
1306 #elif defined (__linux__) || defined (__APPLE__)
1307       path = "/";
1308 #else
1309       path = "/";
1310 #endif
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) *w++ = G_DIR_SEPARATOR;
1320     // terminate string
1321     *w = '\0';
1322
1323 #ifdef FILEDLG_DBG
1324     Sys_Printf("Done.\n");
1325     Sys_Printf("Calling gtk_file_selection_new with title: %s...", title);
1326 #endif
1327
1328     file_sel = gtk_file_selection_new (title);
1329
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", file_sel); 
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), "filename", &filename);
1485   g_object_set_data (G_OBJECT (file_sel), "success", &success);
1486
1487   if (path != NULL)
1488     gtk_file_selection_set_filename (GTK_FILE_SELECTION (file_sel), path);
1489
1490   gtk_grab_add (file_sel);
1491   gtk_widget_show (file_sel);
1492
1493   while (loop)
1494     gtk_main_iteration ();
1495
1496   filename = g_strdup(gtk_file_selection_get_filename (GTK_FILE_SELECTION (file_sel)));
1497
1498   gtk_grab_remove (file_sel);
1499   gtk_widget_destroy (file_sel);
1500
1501   return filename;
1502 }
1503
1504 bool WINAPI color_dialog (void *parent, float *color, const char* title)
1505 {
1506   GtkWidget* dlg;
1507   double clr[3];
1508   int loop = 1, ret = IDCANCEL;
1509
1510   clr[0] = color[0];
1511   clr[1] = color[1];
1512   clr[2] = color[2];
1513
1514   dlg = gtk_color_selection_dialog_new (title);
1515   gtk_color_selection_set_color (GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG (dlg)->colorsel), clr);
1516   gtk_signal_connect (GTK_OBJECT (dlg), "delete_event",
1517                       GTK_SIGNAL_FUNC (dialog_delete_callback), NULL);
1518   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
1519                       GTK_SIGNAL_FUNC (gtk_widget_destroy), NULL);
1520   gtk_signal_connect (GTK_OBJECT (GTK_COLOR_SELECTION_DIALOG (dlg)->ok_button), "clicked",
1521                       GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDOK));
1522   gtk_signal_connect (GTK_OBJECT (GTK_COLOR_SELECTION_DIALOG (dlg)->cancel_button), "clicked",
1523                       GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDCANCEL));
1524   g_object_set_data (G_OBJECT (dlg), "loop", &loop);
1525   g_object_set_data (G_OBJECT (dlg), "ret", &ret);
1526
1527   if (parent != NULL)
1528     gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (parent));
1529
1530   gtk_widget_show (dlg);
1531   gtk_grab_add (dlg);
1532
1533   while (loop)
1534     gtk_main_iteration ();
1535
1536   GdkColor gdkcolor;
1537   gtk_color_selection_get_current_color (GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG (dlg)->colorsel), &gdkcolor);
1538   clr[0] = gdkcolor.red / 65535.0;
1539   clr[1] = gdkcolor.green / 65535.0;
1540   clr[2] = gdkcolor.blue / 65535.0;
1541
1542   gtk_grab_remove (dlg);
1543   gtk_widget_destroy (dlg);
1544
1545   if (ret == IDOK)
1546   {
1547     color[0] = (float)clr[0];
1548     color[1] = (float)clr[1];
1549     color[2] = (float)clr[2];
1550
1551     return true;
1552   }
1553
1554   return false;
1555 }
1556
1557 void OpenURL(const char *url)
1558 {
1559   // let's put a little comment
1560   Sys_Printf("OpenURL: %s\n", url);
1561 #ifdef __linux__
1562   // \todo FIXME: the way we open URLs on *nix should be improved. A script is good (see how I do on RTCW)
1563   char command[2*PATH_MAX];
1564   snprintf( command, sizeof(command), "%s/openurl.sh \"%s\" &", g_strAppPath.GetBuffer(), url );
1565   if (system (command) != 0)
1566     gtk_MessageBox (g_pParentWnd->m_pWidget, "Failed to launch Netscape!");
1567 #endif
1568 #ifdef __APPLE__
1569   char command[2*PATH_MAX];
1570   snprintf (command, sizeof(command),
1571             "open \"%s\" &", url, url);
1572   if (system (command) != 0)
1573     gtk_MessageBox (g_pParentWnd->m_pWidget, "Unable to launch browser!");
1574 #endif
1575 #ifdef _WIN32
1576   ShellExecute( (HWND)GDK_WINDOW_HWND (g_pParentWnd->m_pWidget->window), "open", url, NULL, NULL, SW_SHOW );
1577 #endif
1578 }
1579
1580 void CheckMenuSplitting (GtkWidget *&menu)
1581 {
1582   GtkWidget *item,*menu2;
1583
1584   GtkRequisition requisition;
1585   gint screen_height;
1586
1587   gtk_widget_size_request (GTK_WIDGET (menu), &requisition);
1588   screen_height = gdk_screen_height ();
1589
1590   if ((screen_height - requisition.height) < 20)
1591   {
1592     menu2 = gtk_menu_new ();
1593
1594     // move the last 2 items to a submenu (3 because of win32)
1595     for (int i = 0; i < 3; i++)
1596     {
1597       item = GTK_WIDGET (g_list_last (gtk_container_children (GTK_CONTAINER (menu)))->data);
1598       gtk_widget_ref (item);
1599       gtk_container_remove (GTK_CONTAINER (menu), item);
1600       gtk_menu_append (GTK_MENU (menu2), item);
1601       gtk_widget_unref (item);
1602     }
1603
1604     item = gtk_menu_item_new_with_label ("--------");
1605     gtk_widget_show (item);
1606     gtk_container_add (GTK_CONTAINER (menu), item);
1607     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu2);
1608     menu = menu2;
1609   }
1610 }