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>
36 #include <glib/gi18n.h>
38 #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 gchar *dummy[] = { "1 1 1 1", " c None", " " };
690 printf("calling gdk_pixmap_create_from_xpm_d\n");
691 *gdkpixmap = gdk_pixmap_create_from_xpm_d (gdk_get_default_root_window(), mask, NULL, dummy);
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 gchar *dummy[] = { "1 1 1 1", " c None", " " };
729 *gdkpixmap = gdk_pixmap_create_from_xpm_d (gdk_get_default_root_window(), (GdkBitmap **)mask, NULL, dummy);
737 // Load a xpm file and return a pixmap widget.
738 GtkWidget* new_pixmap (GtkWidget* widget, const 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, const 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, const 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, const 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, const 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 ) {
858 parent = gtk_widget_get_toplevel( widget );
859 loop = (int*)g_object_get_data( G_OBJECT( parent ), "loop" );
860 ret = (int*)g_object_get_data( G_OBJECT( parent ), "ret" );
866 gint dialog_delete_callback (GtkWidget *widget, GdkEvent* event, gpointer data)
870 gtk_widget_hide (widget);
871 loop = (int*)g_object_get_data (G_OBJECT (widget), "loop");
877 gint dialog_url_callback (GtkWidget *widget, GdkEvent* event, gpointer data)
879 OpenURL((const char *)g_object_get_data (G_OBJECT (widget), "URL"));
884 int WINAPI gtk_MessageBox (void *parent, const char* lpText, const char* lpCaption, guint32 uType, const char* URL)
886 GtkWidget *window, *w, *vbox, *hbox;
887 GtkAccelGroup *accel;
888 int mode = (uType & MB_TYPEMASK), ret, loop = 1;
890 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
891 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
892 GTK_SIGNAL_FUNC (dialog_delete_callback), NULL);
893 gtk_signal_connect (GTK_OBJECT (window), "destroy",
894 GTK_SIGNAL_FUNC (gtk_widget_destroy), NULL);
895 gtk_window_set_title (GTK_WINDOW (window), lpCaption);
896 gtk_container_border_width (GTK_CONTAINER (window), 10);
897 g_object_set_data (G_OBJECT (window), "loop", &loop);
898 g_object_set_data (G_OBJECT (window), "ret", &ret);
899 gtk_widget_realize (window);
901 gtk_window_set_policy(GTK_WINDOW (window),FALSE,FALSE,TRUE);
904 gtk_window_set_transient_for (GTK_WINDOW (window), GTK_WINDOW (parent));
906 accel = gtk_accel_group_new ();
907 gtk_window_add_accel_group (GTK_WINDOW (window), accel);
909 vbox = gtk_vbox_new (FALSE, 10);
910 gtk_container_add (GTK_CONTAINER (window), vbox);
911 gtk_widget_show (vbox);
913 w = gtk_label_new (lpText);
914 gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 2);
915 gtk_label_set_justify (GTK_LABEL (w), GTK_JUSTIFY_LEFT);
918 w = gtk_hseparator_new ();
919 gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 2);
922 hbox = gtk_hbox_new (FALSE, 10);
923 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 2);
924 gtk_widget_show (hbox);
928 w = gtk_button_new_with_label ("Ok");
929 gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
930 gtk_signal_connect (GTK_OBJECT (w), "clicked",
931 GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDOK));
932 gtk_widget_add_accelerator (w, "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0);
933 gtk_widget_add_accelerator (w, "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0);
934 GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
935 gtk_widget_grab_default (w);
939 else if (mode == MB_OKCANCEL)
941 w = gtk_button_new_with_label ("Ok");
942 gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
943 gtk_signal_connect (GTK_OBJECT (w), "clicked",
944 GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDOK));
945 gtk_widget_add_accelerator (w, "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0);
946 GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
947 gtk_widget_grab_default (w);
950 w = gtk_button_new_with_label ("Cancel");
951 gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
952 gtk_signal_connect (GTK_OBJECT (w), "clicked",
953 GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDCANCEL));
954 gtk_widget_add_accelerator (w, "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0);
958 else if (mode == MB_YESNOCANCEL)
960 w = gtk_button_new_with_label ("Yes");
961 gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
962 gtk_signal_connect (GTK_OBJECT (w), "clicked",
963 GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDYES));
964 GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
965 gtk_widget_grab_default (w);
968 w = gtk_button_new_with_label ("No");
969 gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
970 gtk_signal_connect (GTK_OBJECT (w), "clicked",
971 GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDNO));
974 w = gtk_button_new_with_label ("Cancel");
975 gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
976 gtk_signal_connect (GTK_OBJECT (w), "clicked",
977 GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDCANCEL));
981 else /* if (mode == MB_YESNO) */
983 w = gtk_button_new_with_label ("Yes");
984 gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
985 gtk_signal_connect (GTK_OBJECT (w), "clicked",
986 GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDYES));
987 GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
988 gtk_widget_grab_default (w);
991 w = gtk_button_new_with_label ("No");
992 gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
993 gtk_signal_connect (GTK_OBJECT (w), "clicked",
994 GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDNO));
1001 w = gtk_button_new_with_label ("Go to URL");
1002 gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
1003 gtk_signal_connect (GTK_OBJECT (w), "clicked",
1004 GTK_SIGNAL_FUNC (dialog_url_callback), NULL);
1005 g_object_set_data (G_OBJECT (w), "URL", (void *)URL);
1006 GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
1007 gtk_widget_grab_default (w);
1008 gtk_widget_show (w);
1012 gtk_widget_show (window);
1013 gtk_grab_add (window);
1016 gtk_main_iteration ();
1018 gtk_grab_remove (window);
1019 gtk_widget_destroy (window);
1024 // =============================================================================
1027 // fenris #3078 WHENHELLISFROZENOVER
1029 //#define FILEDLG_DBG
1031 static void file_sel_callback (GtkWidget *widget, gpointer data)
1037 parent = gtk_widget_get_toplevel (widget);
1038 loop = (int*)g_object_get_data (G_OBJECT (parent), "loop");
1039 success = (bool*)g_object_get_data (G_OBJECT (parent), "success");
1041 if ((int)data == IDOK)
1046 Sys_Printf("file_sel_callback != IDOK\n");
1053 #include <commdlg.h>
1054 static OPENFILENAME ofn; /* common dialog box structure */
1055 static char szDirName[MAX_PATH]; /* directory string */
1056 static char szFile[MAX_PATH]; /* filename string */
1057 static char szFileTitle[MAX_PATH]; /* file title string */
1058 static int i, cbString; /* integer count variables */
1059 static HANDLE hf; /* file handle */
1061 static char szFile[QER_MAX_NAMELEN];
1064 #define FILEDLG_CUSTOM_FILTER_LENGTH 64
1065 // to be used with the advanced file selector
1067 class CFileType : public IFileTypeList
1069 struct filetype_copy_t
1071 void operator=(const filetype_t& other)
1073 m_name = other.name;
1074 m_pattern = other.pattern;
1084 m_strWin32Filters = NULL;
1085 m_pstrGTKMasks = NULL;
1088 virtual ~CFileType()
1091 DestroyWin32Filters();
1095 void addType(filetype_t type)
1097 filetype_copy_t* newTypes = new filetype_copy_t [m_nTypes+1];
1100 for(int i=0; i<m_nTypes; i++)
1101 newTypes[i] = m_pTypes[i];
1104 m_pTypes = newTypes;
1105 m_pTypes[m_nTypes] = type;
1107 ConstructGTKMasks();
1108 ConstructWin32Filters();
1111 filetype_t GetTypeForWin32Filter(const char *filter) const
1113 for(int i=0; i<m_nTypes; i++)
1114 if(strcmp(m_pTypes[i].m_pattern.c_str(), filter)==0)
1115 return filetype_t(m_pTypes[i].m_name.c_str(), m_pTypes[i].m_pattern.c_str());
1116 return filetype_t();
1119 filetype_t GetTypeForGTKMask(const char *mask) const
1121 for(int i=0; i<m_nTypes; i++)
1122 if(strcmp(m_pstrGTKMasks[i],mask)==0)
1123 return filetype_t(m_pTypes[i].m_name.c_str(), m_pTypes[i].m_pattern.c_str());
1124 return filetype_t();
1127 char *m_strWin32Filters;
1128 char **m_pstrGTKMasks;
1131 filetype_copy_t *m_pTypes;
1133 void DestroyWin32Filters()
1135 delete[] m_strWin32Filters;
1138 void ConstructWin32Filters()
1144 DestroyWin32Filters();
1145 for(i=0; i<m_nTypes; i++)
1146 len = len + strlen(m_pTypes[i].m_name.c_str()) + strlen(m_pTypes[i].m_pattern.c_str())*2 + 5;
1147 m_strWin32Filters = new char[len+1]; // length + null char
1148 for(i=0, w = m_strWin32Filters; i<m_nTypes; i++)
1150 for(r = m_pTypes[i].m_name.c_str(); *r!='\0'; r++, w++)
1154 for(r = m_pTypes[i].m_pattern.c_str(); *r!='\0'; r++, w++)
1158 for(r = m_pTypes[i].m_pattern.c_str(); *r!='\0'; r++, w++)
1159 *w = (*r == ',') ? ';' : *r;
1162 m_strWin32Filters[len] = '\0';
1165 void DestroyGTKMasks()
1167 if(m_pstrGTKMasks != NULL)
1168 for(char **p = m_pstrGTKMasks; *p != NULL; p++)
1170 delete[] m_pstrGTKMasks;
1173 void ConstructGTKMasks()
1180 m_pstrGTKMasks = new char*[m_nTypes+1];
1181 for(i=0; i<m_nTypes; i++)
1183 len = strlen(m_pTypes[i].m_name.c_str()) + strlen(m_pTypes[i].m_pattern.c_str()) + 3;
1184 m_pstrGTKMasks[i] = new char[len+1]; // length + null char
1185 w = m_pstrGTKMasks[i];
1186 for(r = m_pTypes[i].m_name.c_str(); *r!='\0'; r++, w++)
1190 for(r = m_pTypes[i].m_pattern.c_str(); *r!='\0'; r++, w++)
1195 m_pstrGTKMasks[m_nTypes] = NULL;
1200 const char* file_dialog (void *parent, gboolean open, const char* title, const char* path, const char* pattern)
1203 GtkWidget* file_sel;
1205 char *new_path = NULL;
1212 GetFileTypeRegistry()->getTypeList(pattern, &typelist);
1215 Sys_Printf("file_dialog: open = %d title = %s path = %s\n", open, title, path);
1218 Sys_Printf("Patterns:\n");
1219 char** p = typelist.m_pstrGTKMasks;
1221 Sys_Printf("%s\n", *p++);
1224 Sys_Printf("no patterns\n");
1228 // win32 dialog stores the selected "save as type" extension in the second null-terminated string
1229 char customfilter[FILEDLG_CUSTOM_FILTER_LENGTH];
1231 if (g_PrefsDlg.m_bNativeGUI)
1234 Sys_Printf("Doing win32 file dialog...");
1236 // do that the native way
1237 /* Place the terminating null character in the szFile. */
1239 customfilter[0] = customfilter[1] = customfilter[2] = '\0';
1241 /* Set the members of the OPENFILENAME structure. */
1242 ofn.lStructSize = sizeof(OPENFILENAME);
1243 ofn.hwndOwner = (HWND)GDK_WINDOW_HWND (g_pParentWnd->m_pWidget->window);
1246 ofn.nFilterIndex = 0;
1247 ofn.lpstrFilter = typelist.m_strWin32Filters;
1249 else ofn.nFilterIndex = 1;
1250 ofn.lpstrCustomFilter = customfilter;
1251 ofn.nMaxCustFilter = sizeof(customfilter);
1252 ofn.lpstrFile = szFile;
1253 ofn.nMaxFile = sizeof(szFile);
1254 ofn.lpstrFileTitle = NULL; // we don't need to get the name of the file
1257 // szDirName: Radiant uses unix convention for paths internally
1258 // Win32 (of course) and Gtk (who would have thought) expect the '\\' convention
1259 // copy path, replacing dir separators as appropriate
1260 for(r=path, w=szDirName; *r!='\0'; r++)
1261 *w++ = (*r=='/') ? '\\' : *r;
1264 ofn.lpstrInitialDir = szDirName;
1266 else ofn.lpstrInitialDir = NULL;
1267 ofn.lpstrTitle = title;
1268 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
1270 /* Display the Open dialog box. */
1271 // it's open or close depending on 'open' parameter
1274 if (!GetOpenFileName(&ofn))
1275 return NULL; // canceled
1279 if (!GetSaveFileName(&ofn))
1280 return NULL; // canceled
1284 type = typelist.GetTypeForWin32Filter(customfilter+1);
1287 Sys_Printf("Done.\n");
1293 // do that the Gtk way
1295 title = open ? _("Open File") : _("Save File");
1298 Sys_Printf("Doing Gtk file dialog:\nBuilding new_path..");
1300 // we expect an actual path below, if the path is NULL we might crash
1301 if (!path || path[0] == '\0')
1305 #elif defined (__linux__) || defined (__APPLE__)
1312 // alloc new path with extra char for dir separator
1313 new_path = new char[strlen(path)+1+1];
1314 // copy path, replacing dir separators as appropriate
1315 for(r=path, w=new_path; *r!='\0'; r++)
1316 *w++ = (*r=='/') ? G_DIR_SEPARATOR : *r;
1317 // add dir separator to end of path if required
1318 if(*(w-1) != G_DIR_SEPARATOR) *w++ = G_DIR_SEPARATOR;
1323 Sys_Printf("Done.\n");
1324 Sys_Printf("Calling gtk_file_selection_new with title: %s...", title);
1327 file_sel = gtk_file_selection_new (title);
1328 gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_sel), "/home/mattn/dev/ufoai/trunk/base/maps/");
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), "success", &success);
1487 gtk_file_selection_set_filename (GTK_FILE_SELECTION (file_sel), path);
1489 gtk_grab_add (file_sel);
1490 gtk_widget_show (file_sel);
1493 gtk_main_iteration ();
1495 filename = g_strdup(gtk_file_selection_get_filename (GTK_FILE_SELECTION (file_sel)));
1497 gtk_grab_remove (file_sel);
1498 gtk_widget_destroy (file_sel);
1503 bool WINAPI color_dialog (void *parent, float *color, const char* title)
1507 int loop = 1, ret = IDCANCEL;
1513 dlg = gtk_color_selection_dialog_new (title);
1514 gtk_color_selection_set_color (GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG (dlg)->colorsel), clr);
1515 gtk_signal_connect (GTK_OBJECT (dlg), "delete_event",
1516 GTK_SIGNAL_FUNC (dialog_delete_callback), NULL);
1517 gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
1518 GTK_SIGNAL_FUNC (gtk_widget_destroy), NULL);
1519 gtk_signal_connect (GTK_OBJECT (GTK_COLOR_SELECTION_DIALOG (dlg)->ok_button), "clicked",
1520 GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDOK));
1521 gtk_signal_connect (GTK_OBJECT (GTK_COLOR_SELECTION_DIALOG (dlg)->cancel_button), "clicked",
1522 GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDCANCEL));
1523 g_object_set_data (G_OBJECT (dlg), "loop", &loop);
1524 g_object_set_data (G_OBJECT (dlg), "ret", &ret);
1527 gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (parent));
1529 gtk_widget_show (dlg);
1533 gtk_main_iteration ();
1536 gtk_color_selection_get_current_color (GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG (dlg)->colorsel), &gdkcolor);
1537 clr[0] = gdkcolor.red / 65535.0;
1538 clr[1] = gdkcolor.green / 65535.0;
1539 clr[2] = gdkcolor.blue / 65535.0;
1541 gtk_grab_remove (dlg);
1542 gtk_widget_destroy (dlg);
1546 color[0] = (float)clr[0];
1547 color[1] = (float)clr[1];
1548 color[2] = (float)clr[2];
1556 void OpenURL(const char *url)
1558 // let's put a little comment
1559 Sys_Printf("OpenURL: %s\n", url);
1561 // \todo FIXME: the way we open URLs on *nix should be improved. A script is good (see how I do on RTCW)
1562 char command[2*PATH_MAX];
1563 snprintf( command, sizeof(command), "%s/openurl.sh \"%s\" &", g_strAppPath.GetBuffer(), url );
1564 if (system (command) != 0)
1565 gtk_MessageBox (g_pParentWnd->m_pWidget, "Failed to launch Netscape!");
1568 char command[2*PATH_MAX];
1569 snprintf (command, sizeof(command),
1570 "open \"%s\" &", url, url);
1571 if (system (command) != 0)
1572 gtk_MessageBox (g_pParentWnd->m_pWidget, "Unable to launch browser!");
1575 ShellExecute( (HWND)GDK_WINDOW_HWND (g_pParentWnd->m_pWidget->window), "open", url, NULL, NULL, SW_SHOW );
1579 void CheckMenuSplitting (GtkWidget *&menu)
1581 GtkWidget *item,*menu2;
1583 GtkRequisition requisition;
1586 gtk_widget_size_request (GTK_WIDGET (menu), &requisition);
1587 screen_height = gdk_screen_height ();
1589 if ((screen_height - requisition.height) < 20)
1591 menu2 = gtk_menu_new ();
1593 // move the last 2 items to a submenu (3 because of win32)
1594 for (int i = 0; i < 3; i++)
1596 item = GTK_WIDGET (g_list_last (gtk_container_children (GTK_CONTAINER (menu)))->data);
1597 gtk_widget_ref (item);
1598 gtk_container_remove (GTK_CONTAINER (menu), item);
1599 gtk_menu_append (GTK_MENU (menu2), item);
1600 gtk_widget_unref (item);
1603 item = gtk_menu_item_new_with_label ("--------");
1604 gtk_widget_show (item);
1605 gtk_container_add (GTK_CONTAINER (menu), item);
1606 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu2);