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