2 Copyright (c) 2001, Loki software, inc.
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
8 Redistributions of source code must retain the above copyright notice, this list
9 of conditions and the following disclaimer.
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.
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
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.
32 // Small functions to help with GTK
35 #include <gdk/gdkkeysyms.h>
37 #if defined (__linux__) || defined (__APPLE__)
45 #include <gdk/gdkwin32.h>
46 #define WIN32_LEAN_AND_MEAN
59 // =============================================================================
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
66 void save_window_pos (GtkWidget *wnd, window_position_t& pos)
68 if ((wnd == NULL) || (wnd->window == NULL))
71 get_window_pos(wnd, &pos.x, &pos.y);
73 pos.w = wnd->allocation.width;
74 pos.h = wnd->allocation.height;
77 //Sys_Printf("save_window_pos 'Window %s'\n",buf);
82 void win32_get_window_pos(GtkWidget *widget, gint *x, gint *y)
84 // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=913
85 if ( g_PrefsDlg.m_bStartOnPrimMon ) {
88 HWND xwnd = (HWND)GDK_WINDOW_HWND (widget->window);
89 const GdkRectangle primaryMonitorRect = g_pParentWnd->GetPrimaryMonitorRect();
91 GetClientRect(xwnd,&rc);
94 ClientToScreen(xwnd,&point);
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);
104 // this is the same as the unix version of get_window_pos
105 gdk_window_get_root_origin (widget->window, x, y);
108 Sys_Printf("win32_get_window_pos %p %d,%d\n",widget,*x,*y);
113 void load_window_pos (GtkWidget *wnd, window_position_t& pos)
116 const GdkRectangle primaryMonitorRect = g_pParentWnd->GetPrimaryMonitorRect();
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);
124 // FIXME: not multihead safe
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);
132 gtk_window_move(GTK_WINDOW(wnd), pos.x, pos.y);
134 gtk_window_set_default_size (GTK_WINDOW (wnd), pos.w, pos.h);
136 Sys_Printf("load_window_pos %p 'Window,%s'\n",wnd,windowData);
140 gint widget_delete_hide (GtkWidget *widget)
142 gtk_widget_hide (widget);
148 // Thanks to Mercury, Fingolfin - ETG
149 int readLongLE(FILE *file, unsigned long *m_bytesRead, int *value)
152 int len = fread(buf, 4, 1, file);
157 *value = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
161 short readShortLE(FILE *file, unsigned long *m_bytesRead, short unsigned *value)
164 int len = fread(buf, 2, 1, file);
169 *value = buf[0] | buf[1] << 8;
173 unsigned char *load_bitmap_file (const char* filename, guint16 *width, guint16 *height)
175 int bmWidth, bmHeight;
176 short unsigned bmPlanes, bmBitsPixel;
178 unsigned char rgbBlue;
179 unsigned char rgbGreen;
180 unsigned char rgbRed;
181 unsigned char rgbReserved;
185 short unsigned res1,res2;
186 int filesize, pixoff;
187 int bmisize, compression;
190 unsigned long m_bytesRead = 0;
191 unsigned char *imagebits = NULL;
194 *width = *height = 0;
196 fp = fopen(filename,"rb");
203 rc = fread(&m1, 1, 1, fp);
211 rc = fread(&m2, 1, 1, fp);
213 if ((m1!='B') || (m2!='M'))
219 if (readLongLE(fp,&m_bytesRead,&filesize)) {
224 if (readShortLE(fp,&m_bytesRead,&res1)) {
229 if (readShortLE(fp,&m_bytesRead,&res2)) {
234 if (readLongLE(fp,&m_bytesRead,&pixoff)) {
239 if (readLongLE(fp,&m_bytesRead,&bmisize)) {
244 if (readLongLE(fp,&m_bytesRead,&bmWidth)) {
249 if (readLongLE(fp,&m_bytesRead,&bmHeight)) {
254 if (readShortLE(fp,&m_bytesRead,&bmPlanes)) {
259 if (readShortLE(fp,&m_bytesRead,&bmBitsPixel)) {
264 if (readLongLE(fp,&m_bytesRead,&compression)) {
269 if (readLongLE(fp,&m_bytesRead,&sizeimage)) {
274 if (readLongLE(fp,&m_bytesRead,&xscale)) {
279 if (readLongLE(fp,&m_bytesRead,&yscale)) {
284 if (readLongLE(fp,&m_bytesRead,&colors)) {
289 if (readLongLE(fp,&m_bytesRead,&impcol)) {
295 colors = 1 << bmBitsPixel;
297 RGBQUAD *colormap = NULL;
298 if (bmBitsPixel != 24)
300 colormap = new RGBQUAD[colors];
301 if (colormap == NULL)
308 for (i = 0; i < colors; i++)
310 unsigned char r ,g, b, dummy;
312 rc = fread(&b, 1, 1, fp);
321 rc = fread(&g, 1, 1, fp);
330 rc = fread(&r, 1, 1, fp);
339 rc = fread(&dummy, 1, 1, fp);
348 colormap[i].rgbRed=r;
349 colormap[i].rgbGreen=g;
350 colormap[i].rgbBlue=b;
354 if ((long)m_bytesRead > pixoff)
361 while ((long)m_bytesRead < pixoff)
364 fread(&dummy,1,1,fp);
371 // set the output params
372 imagebits = (unsigned char *)malloc(w * h * 3);
373 long row_size = w * 3;
375 if (imagebits != NULL)
379 unsigned char *outbuf = imagebits;
383 if (compression == 0) // BI_RGB
385 // read rows in reverse order
386 for (row = bmHeight - 1; row >= 0; row--)
388 // which row are we working on?
389 rowOffset = (long unsigned)row * row_size;
391 if (bmBitsPixel == 24)
393 for (int col=0;col<w;col++)
395 long offset = col * 3;
398 if (fread((void *)(pixel),1,3,fp) == 3)
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
406 m_bytesRead += row_size;
408 // read DWORD padding
409 while ((m_bytesRead - pixoff) & 3)
412 if (fread(&dummy,1,1,fp) != 1)
423 // pixels are packed as 1 , 4 or 8 bit vals. need to unpack them
425 unsigned long mask = (1 << bmBitsPixel) - 1;
426 unsigned char inbyte = 0;
428 for (int col = 0; col < w; col++)
432 // if we need another byte
436 if (fread(&inbyte,1,1,fp) != 1)
446 // keep track of where we are in the bytes
447 bit_count -= bmBitsPixel;
448 pix = ( inbyte >> bit_count) & mask;
450 // lookup the color from the colormap - stuff it in our buffer
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;
457 // read DWORD padding
458 while ((m_bytesRead - pixoff) & 3)
461 if (fread(&dummy,1,1,fp)!=1)
477 unsigned char c, c1 = 0, *pp;
479 pp = outbuf + (bmHeight - 1) * bmWidth * 3;
481 if (bmBitsPixel == 8)
483 while (row < bmHeight)
491 for (i = 0; i < c; x++, i++)
493 *pp = colormap[c1].rgbRed; pp++;
494 *pp = colormap[c1].rgbGreen; pp++;
495 *pp = colormap[c1].rgbBlue; pp++;
500 // c==0x00, escape codes
502 if (c == 0x00) // end of line
506 pp = outbuf + (bmHeight - row - 1) * bmWidth * 3;
510 else if (c == 0x02) // delta
516 pp = outbuf + x*3 + (bmHeight - row - 1) * bmWidth * 3;
518 else // absolute mode
520 for (i = 0; i < c; x++, i++)
523 *pp = colormap[c1].rgbRed; pp++;
524 *pp = colormap[c1].rgbGreen; pp++;
525 *pp = colormap[c1].rgbBlue; pp++;
529 getc(fp); // odd length run: read an extra pad byte
534 else if (bmBitsPixel == 4)
536 while (row < bmHeight)
544 for (i = 0; i < c; x++, i++)
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++;
553 // c==0x00, escape codes
556 if (c == 0x00) // end of line
560 pp = outbuf + (bmHeight - row - 1) * bmWidth * 3;
564 else if (c == 0x02) // delta
570 pp = outbuf + x * 3 + (bmHeight - row - 1) * bmWidth * 3;
572 else // absolute mode
574 for (i = 0; i < c; x++, i++)
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++;
583 if (((c & 3) == 1) || ((c & 3) == 2))
584 getc(fp); // odd length run: read an extra pad byte
598 void bmp_to_pixmap (const char* filename, GdkPixmap **pixmap, GdkBitmap **mask)
600 guint16 width, height;
602 GdkWindow *window = gdk_get_default_root_window();
603 GdkColormap *colormap;
604 GdkGC* gc = gdk_gc_new (window);
606 bool hasMask = false;
608 *pixmap = *mask = NULL;
609 buf = load_bitmap_file (filename, &width, &height);
613 colormap = gdk_drawable_get_colormap (window);
614 *pixmap = gdk_pixmap_new (window, width, height, -1);
622 for (i = 0; i < height; i++)
624 for (j = 0; j < width; j++)
626 unsigned char *p = &buf[(i * width + j) * 3];
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);
636 if (p[0] == 0xFF && p[1] == 0x00 && p[2] == 0xFF)
642 *mask = gdk_pixmap_new (window, width, height, 1);
643 gc = gdk_gc_new (*mask);
646 for (i = 0; i < height; i++)
648 for (j = 0; j < width; j++)
650 GdkColor mask_pattern;
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;
658 mask_pattern.pixel = 1;
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);
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);
678 void load_pixmap (const char* filename, GtkWidget* widget, GdkPixmap **gdkpixmap, GdkBitmap **mask)
682 str = g_strBitmapsPath;
685 bmp_to_pixmap (str.GetBuffer (), gdkpixmap, mask);
686 if (*gdkpixmap == NULL)
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);
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)
701 str = g_strGameToolsPath;
702 str += g_strPluginsDir;
705 bmp_to_pixmap (str.GetBuffer (), (GdkPixmap **)gdkpixmap, (GdkBitmap **)mask);
707 if (*gdkpixmap == NULL)
709 // look in the core plugins
711 str += g_strPluginsDir;
714 bmp_to_pixmap (str.GetBuffer (), (GdkPixmap **)gdkpixmap, (GdkBitmap **)mask);
716 if (*gdkpixmap == NULL)
719 // look in core modules
721 str += g_strModulesDir;
724 bmp_to_pixmap (str.GetBuffer (), (GdkPixmap **)gdkpixmap, (GdkBitmap **)mask);
726 if (*gdkpixmap == NULL)
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);
737 // Load a xpm file and return a pixmap widget.
738 GtkWidget* new_pixmap (GtkWidget* widget, char* filename)
740 GdkPixmap *gdkpixmap;
744 load_pixmap (filename, widget, &gdkpixmap, &mask);
745 pixmap = gtk_pixmap_new (gdkpixmap, mask);
747 gdk_drawable_unref (gdkpixmap);
748 gdk_drawable_unref (mask);
753 // =============================================================================
756 GtkWidget* menu_separator (GtkWidget *menu)
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);
765 GtkWidget* menu_tearoff (GtkWidget *menu)
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);
774 GtkWidget* create_sub_menu_with_mnemonic (GtkWidget *bar, gchar *mnemonic)
776 GtkWidget *item, *sub_menu;
778 item = gtk_menu_item_new_with_mnemonic (mnemonic);
779 gtk_widget_show (item);
780 gtk_container_add (GTK_CONTAINER (bar), item);
782 sub_menu = gtk_menu_new ();
783 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), sub_menu);
788 extern void AddMenuItem (GtkWidget* menu, unsigned int id);
790 GtkWidget* create_menu_item_with_mnemonic (GtkWidget *menu, gchar *mnemonic, GtkSignalFunc func, int id)
794 item = gtk_menu_item_new_with_mnemonic (mnemonic);
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));
800 AddMenuItem (item, id);
804 GtkWidget* create_check_menu_item_with_mnemonic (GtkWidget *menu, gchar *mnemonic, GtkSignalFunc func, int id, gboolean active)
808 item = gtk_check_menu_item_new_with_mnemonic(mnemonic);
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));
815 AddMenuItem (item, id);
819 GtkWidget* create_radio_menu_item_with_mnemonic (GtkWidget *menu, GtkWidget *last, gchar *mnemonic, GtkSignalFunc func, int id, gboolean state)
822 GSList *group = (GSList*)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);
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));
833 AddMenuItem (item, id);
837 GtkWidget* create_menu_in_menu_with_mnemonic (GtkWidget *menu, const gchar *mnemonic)
839 GtkWidget *item, *submenu;
841 item = gtk_menu_item_new_with_mnemonic(mnemonic);
842 gtk_widget_show (item);
843 gtk_container_add (GTK_CONTAINER (menu), item);
845 submenu = gtk_menu_new ();
846 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
851 // =============================================================================
854 void dialog_button_callback (GtkWidget *widget, gpointer data)
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");
867 gint dialog_delete_callback (GtkWidget *widget, GdkEvent* event, gpointer data)
871 gtk_widget_hide (widget);
872 loop = (int*)g_object_get_data (G_OBJECT (widget), "loop");
878 gint dialog_url_callback (GtkWidget *widget, GdkEvent* event, gpointer data)
880 OpenURL((const char *)g_object_get_data (G_OBJECT (widget), "URL"));
885 int WINAPI gtk_MessageBox (void *parent, const char* lpText, const char* lpCaption, guint32 uType, const char* URL)
887 GtkWidget *window, *w, *vbox, *hbox;
888 GtkAccelGroup *accel;
889 int mode = (uType & MB_TYPEMASK), ret, loop = 1;
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);
902 gtk_window_set_policy(GTK_WINDOW (window),FALSE,FALSE,TRUE);
905 gtk_window_set_transient_for (GTK_WINDOW (window), GTK_WINDOW (parent));
907 accel = gtk_accel_group_new ();
908 gtk_window_add_accel_group (GTK_WINDOW (window), accel);
910 vbox = gtk_vbox_new (FALSE, 10);
911 gtk_container_add (GTK_CONTAINER (window), vbox);
912 gtk_widget_show (vbox);
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);
919 w = gtk_hseparator_new ();
920 gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 2);
923 hbox = gtk_hbox_new (FALSE, 10);
924 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 2);
925 gtk_widget_show (hbox);
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);
940 else if (mode == MB_OKCANCEL)
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);
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);
959 else if (mode == MB_YESNOCANCEL)
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);
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));
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));
982 else /* if (mode == MB_YESNO) */
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);
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));
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);
1013 gtk_widget_show (window);
1014 gtk_grab_add (window);
1017 gtk_main_iteration ();
1019 gtk_grab_remove (window);
1020 gtk_widget_destroy (window);
1025 // =============================================================================
1028 // fenris #3078 WHENHELLISFROZENOVER
1030 //#define FILEDLG_DBG
1032 static void file_sel_callback (GtkWidget *widget, gpointer data)
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");
1042 if ((int)data == IDOK)
1047 Sys_Printf("file_sel_callback != IDOK\n");
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 */
1062 static char szFile[QER_MAX_NAMELEN];
1065 #define FILEDLG_CUSTOM_FILTER_LENGTH 64
1066 // to be used with the advanced file selector
1068 class CFileType : public IFileTypeList
1070 struct filetype_copy_t
1072 void operator=(const filetype_t& other)
1074 m_name = other.name;
1075 m_pattern = other.pattern;
1085 m_strWin32Filters = NULL;
1086 m_pstrGTKMasks = NULL;
1089 virtual ~CFileType()
1092 DestroyWin32Filters();
1096 void addType(filetype_t type)
1098 filetype_copy_t* newTypes = new filetype_copy_t [m_nTypes+1];
1101 for(int i=0; i<m_nTypes; i++)
1102 newTypes[i] = m_pTypes[i];
1105 m_pTypes = newTypes;
1106 m_pTypes[m_nTypes] = type;
1108 ConstructGTKMasks();
1109 ConstructWin32Filters();
1112 filetype_t GetTypeForWin32Filter(const char *filter) const
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();
1120 filetype_t GetTypeForGTKMask(const char *mask) const
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();
1128 char *m_strWin32Filters;
1129 char **m_pstrGTKMasks;
1132 filetype_copy_t *m_pTypes;
1134 void DestroyWin32Filters()
1136 delete[] m_strWin32Filters;
1139 void ConstructWin32Filters()
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++)
1151 for(r = m_pTypes[i].m_name.c_str(); *r!='\0'; r++, w++)
1155 for(r = m_pTypes[i].m_pattern.c_str(); *r!='\0'; r++, w++)
1159 for(r = m_pTypes[i].m_pattern.c_str(); *r!='\0'; r++, w++)
1160 *w = (*r == ',') ? ';' : *r;
1163 m_strWin32Filters[len] = '\0';
1166 void DestroyGTKMasks()
1168 if(m_pstrGTKMasks != NULL)
1169 for(char **p = m_pstrGTKMasks; *p != NULL; p++)
1171 delete[] m_pstrGTKMasks;
1174 void ConstructGTKMasks()
1181 m_pstrGTKMasks = new char*[m_nTypes+1];
1182 for(i=0; i<m_nTypes; i++)
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++)
1191 for(r = m_pTypes[i].m_pattern.c_str(); *r!='\0'; r++, w++)
1196 m_pstrGTKMasks[m_nTypes] = NULL;
1201 const char* file_dialog (void *parent, gboolean open, const char* title, const char* path, const char* pattern)
1204 GtkWidget* file_sel;
1206 char *new_path = NULL;
1213 GetFileTypeRegistry()->getTypeList(pattern, &typelist);
1216 Sys_Printf("file_dialog: open = %d title = %s path = %s\n", open, title, path);
1219 Sys_Printf("Patterns:\n");
1220 char** p = typelist.m_pstrGTKMasks;
1222 Sys_Printf("%s\n", *p++);
1225 Sys_Printf("no patterns\n");
1229 // win32 dialog stores the selected "save as type" extension in the second null-terminated string
1230 char customfilter[FILEDLG_CUSTOM_FILTER_LENGTH];
1232 if (g_PrefsDlg.m_bNativeGUI)
1235 Sys_Printf("Doing win32 file dialog...");
1237 // do that the native way
1238 /* Place the terminating null character in the szFile. */
1240 customfilter[0] = customfilter[1] = customfilter[2] = '\0';
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);
1247 ofn.nFilterIndex = 0;
1248 ofn.lpstrFilter = typelist.m_strWin32Filters;
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
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;
1265 ofn.lpstrInitialDir = szDirName;
1267 else ofn.lpstrInitialDir = NULL;
1268 ofn.lpstrTitle = title;
1269 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
1271 /* Display the Open dialog box. */
1272 // it's open or close depending on 'open' parameter
1275 if (!GetOpenFileName(&ofn))
1276 return NULL; // canceled
1280 if (!GetSaveFileName(&ofn))
1281 return NULL; // canceled
1285 type = typelist.GetTypeForWin32Filter(customfilter+1);
1288 Sys_Printf("Done.\n");
1294 // do that the Gtk way
1296 title = open ? "Open File" : "Save File";
1299 Sys_Printf("Doing Gtk file dialog:\nBuilding new_path..");
1301 // we expect an actual path below, if the path is NULL we might crash
1302 if (!path || path[0] == '\0')
1306 #elif defined (__linux__) || defined (__APPLE__)
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;
1324 Sys_Printf("Done.\n");
1325 Sys_Printf("Calling gtk_file_selection_new with title: %s...", title);
1328 file_sel = gtk_file_selection_new (title);
1331 Sys_Printf("Done.\n");
1332 Sys_Printf("Set the masks...");
1335 #if 0 //!\todo Add masks to GtkFileSelection in gtk-2.0
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));
1345 Sys_Printf("Done.\n");
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));
1357 gtk_window_set_transient_for (GTK_WINDOW (file_sel), GTK_WINDOW (parent));
1360 Sys_Printf("set_data...");
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);
1366 Sys_Printf("Done.\n");
1372 Sys_Printf("set_data \"overwrite\" ...");
1374 g_object_set_data (G_OBJECT (file_sel), "overwrite", GINT_TO_POINTER (1));
1376 Sys_Printf("Done.\n");
1380 if (new_path != NULL)
1383 Sys_Printf("gtk_file_selection_set_filename... %p", file_sel);
1385 gtk_file_selection_set_filename (GTK_FILE_SELECTION (file_sel), new_path);
1388 Sys_Printf("Done.\n");
1392 gtk_grab_add (file_sel);
1394 Sys_Printf("gtk_widget_show... %p", file_sel);
1396 gtk_widget_show (file_sel);
1398 Sys_Printf("Done.\n");
1402 Sys_Printf("gtk_main_iteration...");
1405 gtk_main_iteration ();
1408 #if 0 //!\todo Add masks to GtkFileSelection in gtk2
1410 type = typelist.GetTypeForGTKMask(GTK_FILE_SELECTION (file_sel)->mask);
1412 strcpy(szFile, gtk_file_selection_get_filename (GTK_FILE_SELECTION (file_sel)));
1415 Sys_Printf("Done.\n");
1418 gtk_grab_remove (file_sel);
1419 gtk_widget_destroy (file_sel);
1424 // don't return an empty filename
1425 if(szFile[0] == '\0') return NULL;
1427 // convert back to unix format
1428 for(w=szFile; *w!='\0'; w++)
1433 if (g_PrefsDlg.m_bNativeGUI) // filetype mask not supported in gtk dialog yet
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)
1439 // last ext separator
1440 w = strrchr(szFile, '.');
1442 w = (w!=NULL) ? w : szFile+strlen(szFile);
1443 strcpy(w, type.pattern+1);
1448 // prompt to overwrite existing files
1450 if (access (szFile, R_OK) == 0)
1451 if (gtk_MessageBox (parent, "File already exists.\nOverwrite?", "GtkRadiant", MB_YESNO) == IDNO)
1455 // ... let's use a static filename
1456 Sys_Printf("filename: %p\n", szFile);
1462 char* WINAPI dir_dialog (void *parent, const char* title, const char* path)
1464 GtkWidget* file_sel;
1465 char* filename = (char*)NULL;
1467 bool success = false;
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));
1479 gtk_window_set_transient_for (GTK_WINDOW (file_sel), GTK_WINDOW (parent));
1481 gtk_widget_hide (GTK_FILE_SELECTION (file_sel)->file_list->parent);
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);
1488 gtk_file_selection_set_filename (GTK_FILE_SELECTION (file_sel), path);
1490 gtk_grab_add (file_sel);
1491 gtk_widget_show (file_sel);
1494 gtk_main_iteration ();
1496 filename = g_strdup(gtk_file_selection_get_filename (GTK_FILE_SELECTION (file_sel)));
1498 gtk_grab_remove (file_sel);
1499 gtk_widget_destroy (file_sel);
1504 bool WINAPI color_dialog (void *parent, float *color, const char* title)
1508 int loop = 1, ret = IDCANCEL;
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);
1528 gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (parent));
1530 gtk_widget_show (dlg);
1534 gtk_main_iteration ();
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;
1542 gtk_grab_remove (dlg);
1543 gtk_widget_destroy (dlg);
1547 color[0] = (float)clr[0];
1548 color[1] = (float)clr[1];
1549 color[2] = (float)clr[2];
1557 void OpenURL(const char *url)
1559 // let's put a little comment
1560 Sys_Printf("OpenURL: %s\n", url);
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!");
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!");
1576 ShellExecute( (HWND)GDK_WINDOW_HWND (g_pParentWnd->m_pWidget->window), "open", url, NULL, NULL, SW_SHOW );
1580 void CheckMenuSplitting (GtkWidget *&menu)
1582 GtkWidget *item,*menu2;
1584 GtkRequisition requisition;
1587 gtk_widget_size_request (GTK_WIDGET (menu), &requisition);
1588 screen_height = gdk_screen_height ();
1590 if ((screen_height - requisition.height) < 20)
1592 menu2 = gtk_menu_new ();
1594 // move the last 2 items to a submenu (3 because of win32)
1595 for (int i = 0; i < 3; i++)
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);
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);