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 if ( g_PrefsDlg.m_bStartOnPrimMon ) {
87 HWND xwnd = (HWND)GDK_WINDOW_HWND (widget->window);
88 const GdkRectangle primaryMonitorRect = g_pParentWnd->GetPrimaryMonitorRect();
90 GetClientRect(xwnd,&rc);
93 ClientToScreen(xwnd,&point);
98 *x=max(*x,-widget->allocation.width+10);
99 *x=min(*x,primaryMonitorRect.width-10);
100 *y=max(*y,-widget->allocation.height+10);
101 *y=min(*y,primaryMonitorRect.height-10);
103 // this is the same as the unix version of get_window_pos
104 gdk_window_get_root_origin (widget->window, x, y);
107 Sys_Printf("win32_get_window_pos %p %d,%d\n",widget,*x,*y);
112 void load_window_pos (GtkWidget *wnd, window_position_t& pos)
115 const GdkRectangle primaryMonitorRect = g_pParentWnd->GetPrimaryMonitorRect();
117 if(pos.x < primaryMonitorRect.x
118 || pos.y < primaryMonitorRect.y
119 || pos.x > primaryMonitorRect.x + primaryMonitorRect.width
120 || pos.y > primaryMonitorRect.y + primaryMonitorRect.height)
121 gtk_window_set_position(GTK_WINDOW(wnd), GTK_WIN_POS_CENTER_ON_PARENT);
123 // FIXME: not multihead safe
126 || pos.x > gdk_screen_width ()
127 || pos.y > gdk_screen_height ())
128 gtk_window_set_position(GTK_WINDOW(wnd), GTK_WIN_POS_CENTER_ON_PARENT);
131 gtk_window_move(GTK_WINDOW(wnd), pos.x, pos.y);
133 gtk_window_set_default_size (GTK_WINDOW (wnd), pos.w, pos.h);
135 Sys_Printf("load_window_pos %p 'Window,%s'\n",wnd,windowData);
139 gint widget_delete_hide (GtkWidget *widget)
141 gtk_widget_hide (widget);
147 // Thanks to Mercury, Fingolfin - ETG
148 int readLongLE(FILE *file, unsigned long *m_bytesRead, int *value)
151 int len = fread(buf, 4, 1, file);
156 *value = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
160 short readShortLE(FILE *file, unsigned long *m_bytesRead, short unsigned *value)
163 int len = fread(buf, 2, 1, file);
168 *value = buf[0] | buf[1] << 8;
172 unsigned char *load_bitmap_file (const char* filename, guint16 *width, guint16 *height)
174 int bmWidth, bmHeight;
175 short unsigned bmPlanes, bmBitsPixel;
177 unsigned char rgbBlue;
178 unsigned char rgbGreen;
179 unsigned char rgbRed;
180 unsigned char rgbReserved;
184 short unsigned res1,res2;
185 int filesize, pixoff;
186 int bmisize, compression;
189 unsigned long m_bytesRead = 0;
190 unsigned char *imagebits = NULL;
193 *width = *height = 0;
195 fp = fopen(filename,"rb");
202 rc = fread(&m1, 1, 1, fp);
210 rc = fread(&m2, 1, 1, fp);
212 if ((m1!='B') || (m2!='M'))
218 if (readLongLE(fp,&m_bytesRead,&filesize)) {
223 if (readShortLE(fp,&m_bytesRead,&res1)) {
228 if (readShortLE(fp,&m_bytesRead,&res2)) {
233 if (readLongLE(fp,&m_bytesRead,&pixoff)) {
238 if (readLongLE(fp,&m_bytesRead,&bmisize)) {
243 if (readLongLE(fp,&m_bytesRead,&bmWidth)) {
248 if (readLongLE(fp,&m_bytesRead,&bmHeight)) {
253 if (readShortLE(fp,&m_bytesRead,&bmPlanes)) {
258 if (readShortLE(fp,&m_bytesRead,&bmBitsPixel)) {
263 if (readLongLE(fp,&m_bytesRead,&compression)) {
268 if (readLongLE(fp,&m_bytesRead,&sizeimage)) {
273 if (readLongLE(fp,&m_bytesRead,&xscale)) {
278 if (readLongLE(fp,&m_bytesRead,&yscale)) {
283 if (readLongLE(fp,&m_bytesRead,&colors)) {
288 if (readLongLE(fp,&m_bytesRead,&impcol)) {
294 colors = 1 << bmBitsPixel;
296 RGBQUAD *colormap = NULL;
297 if (bmBitsPixel != 24)
299 colormap = new RGBQUAD[colors];
300 if (colormap == NULL)
307 for (i = 0; i < colors; i++)
309 unsigned char r ,g, b, dummy;
311 rc = fread(&b, 1, 1, fp);
320 rc = fread(&g, 1, 1, fp);
329 rc = fread(&r, 1, 1, fp);
338 rc = fread(&dummy, 1, 1, fp);
347 colormap[i].rgbRed=r;
348 colormap[i].rgbGreen=g;
349 colormap[i].rgbBlue=b;
353 if ((long)m_bytesRead > pixoff)
360 while ((long)m_bytesRead < pixoff)
363 fread(&dummy,1,1,fp);
370 // set the output params
371 imagebits = (unsigned char *)malloc(w * h * 3);
372 long row_size = w * 3;
374 if (imagebits != NULL)
378 unsigned char *outbuf = imagebits;
382 if (compression == 0) // BI_RGB
384 // read rows in reverse order
385 for (row = bmHeight - 1; row >= 0; row--)
387 // which row are we working on?
388 rowOffset = (long unsigned)row * row_size;
390 if (bmBitsPixel == 24)
392 for (int col=0;col<w;col++)
394 long offset = col * 3;
397 if (fread((void *)(pixel),1,3,fp) == 3)
399 // we swap red and blue here
400 *(outbuf + rowOffset + offset + 0) = pixel[2]; // r
401 *(outbuf + rowOffset + offset + 1) = pixel[1]; // g
402 *(outbuf + rowOffset + offset + 2) = pixel[0]; // b
405 m_bytesRead += row_size;
407 // read DWORD padding
408 while ((m_bytesRead - pixoff) & 3)
411 if (fread(&dummy,1,1,fp) != 1)
422 // pixels are packed as 1 , 4 or 8 bit vals. need to unpack them
424 unsigned long mask = (1 << bmBitsPixel) - 1;
425 unsigned char inbyte = 0;
427 for (int col = 0; col < w; col++)
431 // if we need another byte
435 if (fread(&inbyte,1,1,fp) != 1)
445 // keep track of where we are in the bytes
446 bit_count -= bmBitsPixel;
447 pix = ( inbyte >> bit_count) & mask;
449 // lookup the color from the colormap - stuff it in our buffer
451 *(outbuf + rowOffset + col * 3 + 2) = colormap[pix].rgbBlue;
452 *(outbuf + rowOffset + col * 3 + 1) = colormap[pix].rgbGreen;
453 *(outbuf + rowOffset + col * 3 + 0) = colormap[pix].rgbRed;
456 // read DWORD padding
457 while ((m_bytesRead - pixoff) & 3)
460 if (fread(&dummy,1,1,fp)!=1)
476 unsigned char c, c1 = 0, *pp;
478 pp = outbuf + (bmHeight - 1) * bmWidth * 3;
480 if (bmBitsPixel == 8)
482 while (row < bmHeight)
490 for (i = 0; i < c; x++, i++)
492 *pp = colormap[c1].rgbRed; pp++;
493 *pp = colormap[c1].rgbGreen; pp++;
494 *pp = colormap[c1].rgbBlue; pp++;
499 // c==0x00, escape codes
501 if (c == 0x00) // end of line
505 pp = outbuf + (bmHeight - row - 1) * bmWidth * 3;
509 else if (c == 0x02) // delta
515 pp = outbuf + x*3 + (bmHeight - row - 1) * bmWidth * 3;
517 else // absolute mode
519 for (i = 0; i < c; x++, i++)
522 *pp = colormap[c1].rgbRed; pp++;
523 *pp = colormap[c1].rgbGreen; pp++;
524 *pp = colormap[c1].rgbBlue; pp++;
528 getc(fp); // odd length run: read an extra pad byte
533 else if (bmBitsPixel == 4)
535 while (row < bmHeight)
543 for (i = 0; i < c; x++, i++)
545 *pp = colormap[(i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f)].rgbRed; pp++;
546 *pp = colormap[(i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f)].rgbGreen; pp++;
547 *pp = colormap[(i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f)].rgbBlue; pp++;
552 // c==0x00, escape codes
555 if (c == 0x00) // end of line
559 pp = outbuf + (bmHeight - row - 1) * bmWidth * 3;
563 else if (c == 0x02) // delta
569 pp = outbuf + x * 3 + (bmHeight - row - 1) * bmWidth * 3;
571 else // absolute mode
573 for (i = 0; i < c; x++, i++)
577 *pp = colormap[(i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f)].rgbRed; pp++;
578 *pp = colormap[(i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f)].rgbGreen; pp++;
579 *pp = colormap[(i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f)].rgbBlue; pp++;
582 if (((c & 3) == 1) || ((c & 3) == 2))
583 getc(fp); // odd length run: read an extra pad byte
597 void bmp_to_pixmap (const char* filename, GdkPixmap **pixmap, GdkBitmap **mask)
599 guint16 width, height;
601 GdkWindow *window = gdk_get_default_root_window();
602 GdkColormap *colormap;
603 GdkGC* gc = gdk_gc_new (window);
605 bool hasMask = false;
607 *pixmap = *mask = NULL;
608 buf = load_bitmap_file (filename, &width, &height);
612 colormap = gdk_drawable_get_colormap (window);
613 *pixmap = gdk_pixmap_new (window, width, height, -1);
621 for (i = 0; i < height; i++)
623 for (j = 0; j < width; j++)
625 unsigned char *p = &buf[(i * width + j) * 3];
628 pe.c.red = (gushort)(p[0] * 0xFF);
629 pe.c.green = (gushort)(p[1] * 0xFF);
630 pe.c.blue = (gushort)(p[2] * 0xFF);
631 gdk_colormap_alloc_color(colormap, &pe.c, FALSE, TRUE);
632 gdk_gc_set_foreground(gc, &pe.c);
633 gdk_draw_point(*pixmap, gc, j, i);
635 if (p[0] == 0xFF && p[1] == 0x00 && p[2] == 0xFF)
641 *mask = gdk_pixmap_new (window, width, height, 1);
642 gc = gdk_gc_new (*mask);
645 for (i = 0; i < height; i++)
647 for (j = 0; j < width; j++)
649 GdkColor mask_pattern;
651 // pink is transparent
652 if ((buf[(i*width+j)*3] == 0xff) &&
653 (buf[(i*width+j)*3+1] == 0x00) &&
654 (buf[(i*width+j)*3+2] == 0xff))
655 mask_pattern.pixel = 0;
657 mask_pattern.pixel = 1;
659 gdk_gc_set_foreground (gc, &mask_pattern);
660 // possible Win32 Gtk bug here
661 //gdk_draw_point (*mask, gc, j, i);
662 gdk_draw_line (*mask, gc, j, i, j + 1, i);
668 GdkColor mask_pattern;
669 mask_pattern.pixel = 1;
670 gdk_gc_set_foreground (gc, &mask_pattern);
671 gdk_draw_rectangle (*mask, gc, 1, 0, 0, width, height);
677 void load_pixmap (const char* filename, GtkWidget* widget, GdkPixmap **gdkpixmap, GdkBitmap **mask)
681 str = g_strBitmapsPath;
684 bmp_to_pixmap (str.GetBuffer (), gdkpixmap, mask);
685 if (*gdkpixmap == NULL)
687 printf("gdkpixmap was null\n");
688 gchar *dummy[] = { "1 1 1 1", " c None", " " };
689 printf("calling gdk_pixmap_create_from_xpm_d\n");
690 *gdkpixmap = gdk_pixmap_create_from_xpm_d (gdk_get_default_root_window(), mask, NULL, dummy);
694 // this is the same as above but used by the plugins
695 // GdkPixmap **gdkpixmap, GdkBitmap **mask
696 bool WINAPI load_plugin_bitmap (const char* filename, void **gdkpixmap, void **mask)
700 str = g_strGameToolsPath;
701 str += g_strPluginsDir;
704 bmp_to_pixmap (str.GetBuffer (), (GdkPixmap **)gdkpixmap, (GdkBitmap **)mask);
706 if (*gdkpixmap == NULL)
708 // look in the core plugins
710 str += g_strPluginsDir;
713 bmp_to_pixmap (str.GetBuffer (), (GdkPixmap **)gdkpixmap, (GdkBitmap **)mask);
715 if (*gdkpixmap == NULL)
718 // look in core modules
720 str += g_strModulesDir;
723 bmp_to_pixmap (str.GetBuffer (), (GdkPixmap **)gdkpixmap, (GdkBitmap **)mask);
725 if (*gdkpixmap == NULL)
727 gchar *dummy[] = { "1 1 1 1", " c None", " " };
728 *gdkpixmap = gdk_pixmap_create_from_xpm_d (gdk_get_default_root_window(), (GdkBitmap **)mask, NULL, dummy);
736 // Load a xpm file and return a pixmap widget.
737 GtkWidget* new_pixmap (GtkWidget* widget, const char* filename)
739 GdkPixmap *gdkpixmap;
743 load_pixmap (filename, widget, &gdkpixmap, &mask);
744 pixmap = gtk_pixmap_new (gdkpixmap, mask);
746 gdk_drawable_unref (gdkpixmap);
747 gdk_drawable_unref (mask);
752 // =============================================================================
755 GtkWidget* menu_separator (GtkWidget *menu)
757 GtkWidget *menu_item = gtk_menu_item_new ();
758 gtk_menu_append (GTK_MENU (menu), menu_item);
759 gtk_widget_set_sensitive (menu_item, FALSE);
760 gtk_widget_show (menu_item);
764 GtkWidget* menu_tearoff (GtkWidget *menu)
766 GtkWidget *menu_item = gtk_tearoff_menu_item_new ();
767 gtk_menu_append (GTK_MENU (menu), menu_item);
768 // gtk_widget_set_sensitive (menu_item, FALSE); -- controls whether menu is detachable
769 gtk_widget_show (menu_item);
773 GtkWidget* create_sub_menu_with_mnemonic (GtkWidget *bar, const gchar *mnemonic)
775 GtkWidget *item, *sub_menu;
777 item = gtk_menu_item_new_with_mnemonic (mnemonic);
778 gtk_widget_show (item);
779 gtk_container_add (GTK_CONTAINER (bar), item);
781 sub_menu = gtk_menu_new ();
782 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), sub_menu);
787 extern void AddMenuItem (GtkWidget* menu, unsigned int id);
789 GtkWidget* create_menu_item_with_mnemonic (GtkWidget *menu, const gchar *mnemonic, GtkSignalFunc func, int id)
793 item = gtk_menu_item_new_with_mnemonic (mnemonic);
795 gtk_widget_show (item);
796 gtk_container_add (GTK_CONTAINER (menu), item);
797 gtk_signal_connect (GTK_OBJECT (item), "activate", GTK_SIGNAL_FUNC (func), GINT_TO_POINTER (id));
799 AddMenuItem (item, id);
803 GtkWidget* create_check_menu_item_with_mnemonic (GtkWidget *menu, const gchar *mnemonic, GtkSignalFunc func, int id, gboolean active)
807 item = gtk_check_menu_item_new_with_mnemonic(mnemonic);
809 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), active);
810 gtk_widget_show (item);
811 gtk_container_add (GTK_CONTAINER (menu), item);
812 gtk_signal_connect (GTK_OBJECT (item), "activate", GTK_SIGNAL_FUNC (func), GINT_TO_POINTER (id));
814 AddMenuItem (item, id);
818 GtkWidget* create_radio_menu_item_with_mnemonic (GtkWidget *menu, GtkWidget *last, const gchar *mnemonic, GtkSignalFunc func, int id, gboolean state)
821 GSList *group = (GSList*)NULL;
824 group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (last));
825 item = gtk_radio_menu_item_new_with_mnemonic (group, mnemonic);
826 gtk_check_menu_item_set_state (GTK_CHECK_MENU_ITEM (item), state);
828 gtk_widget_show (item);
829 gtk_container_add (GTK_CONTAINER (menu), item);
830 gtk_signal_connect (GTK_OBJECT (item), "activate", GTK_SIGNAL_FUNC (func), GINT_TO_POINTER (id));
832 AddMenuItem (item, id);
836 GtkWidget* create_menu_in_menu_with_mnemonic (GtkWidget *menu, const gchar *mnemonic)
838 GtkWidget *item, *submenu;
840 item = gtk_menu_item_new_with_mnemonic(mnemonic);
841 gtk_widget_show (item);
842 gtk_container_add (GTK_CONTAINER (menu), item);
844 submenu = gtk_menu_new ();
845 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
850 // =============================================================================
853 void dialog_button_callback( GtkWidget *widget, gpointer data ) {
857 parent = gtk_widget_get_toplevel( widget );
858 loop = (int*)g_object_get_data( G_OBJECT( parent ), "loop" );
859 ret = (int*)g_object_get_data( G_OBJECT( parent ), "ret" );
862 *ret = GPOINTER_TO_INT (data);
865 gint dialog_delete_callback (GtkWidget *widget, GdkEvent* event, gpointer data)
869 gtk_widget_hide (widget);
870 loop = (int*)g_object_get_data (G_OBJECT (widget), "loop");
876 gint dialog_url_callback (GtkWidget *widget, GdkEvent* event, gpointer data)
878 OpenURL((const char *)g_object_get_data (G_OBJECT (widget), "URL"));
883 int WINAPI gtk_MessageBox (void *parent, const char* lpText, const char* lpCaption, guint32 uType, const char* URL)
885 GtkWidget *window, *w, *vbox, *hbox;
886 GtkAccelGroup *accel;
887 int mode = (uType & MB_TYPEMASK), ret, loop = 1;
889 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
890 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
891 GTK_SIGNAL_FUNC (dialog_delete_callback), NULL);
892 gtk_signal_connect (GTK_OBJECT (window), "destroy",
893 GTK_SIGNAL_FUNC (gtk_widget_destroy), NULL);
894 gtk_window_set_title (GTK_WINDOW (window), lpCaption);
895 gtk_container_border_width (GTK_CONTAINER (window), 10);
896 g_object_set_data (G_OBJECT (window), "loop", &loop);
897 g_object_set_data (G_OBJECT (window), "ret", &ret);
898 gtk_widget_realize (window);
900 gtk_window_set_policy(GTK_WINDOW (window),FALSE,FALSE,TRUE);
903 gtk_window_set_transient_for (GTK_WINDOW (window), GTK_WINDOW (parent));
905 accel = gtk_accel_group_new ();
906 gtk_window_add_accel_group (GTK_WINDOW (window), accel);
908 vbox = gtk_vbox_new (FALSE, 10);
909 gtk_container_add (GTK_CONTAINER (window), vbox);
910 gtk_widget_show (vbox);
912 w = gtk_label_new (lpText);
913 gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 2);
914 gtk_label_set_justify (GTK_LABEL (w), GTK_JUSTIFY_LEFT);
917 w = gtk_hseparator_new ();
918 gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 2);
921 hbox = gtk_hbox_new (FALSE, 10);
922 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 2);
923 gtk_widget_show (hbox);
927 w = gtk_button_new_with_label (_("Ok"));
928 gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
929 gtk_signal_connect (GTK_OBJECT (w), "clicked",
930 GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDOK));
931 gtk_widget_add_accelerator (w, "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0);
932 gtk_widget_add_accelerator (w, "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0);
933 GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
934 gtk_widget_grab_default (w);
938 else if (mode == MB_OKCANCEL)
940 w = gtk_button_new_with_label (_("Ok"));
941 gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
942 gtk_signal_connect (GTK_OBJECT (w), "clicked",
943 GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDOK));
944 gtk_widget_add_accelerator (w, "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0);
945 GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
946 gtk_widget_grab_default (w);
949 w = gtk_button_new_with_label (_("Cancel"));
950 gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
951 gtk_signal_connect (GTK_OBJECT (w), "clicked",
952 GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDCANCEL));
953 gtk_widget_add_accelerator (w, "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0);
957 else if (mode == MB_YESNOCANCEL)
959 w = gtk_button_new_with_label (_("Yes"));
960 gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
961 gtk_signal_connect (GTK_OBJECT (w), "clicked",
962 GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDYES));
963 GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
964 gtk_widget_grab_default (w);
967 w = gtk_button_new_with_label (_("No"));
968 gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
969 gtk_signal_connect (GTK_OBJECT (w), "clicked",
970 GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDNO));
973 w = gtk_button_new_with_label (_("Cancel"));
974 gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
975 gtk_signal_connect (GTK_OBJECT (w), "clicked",
976 GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDCANCEL));
980 else /* if (mode == MB_YESNO) */
982 w = gtk_button_new_with_label (_("Yes"));
983 gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
984 gtk_signal_connect (GTK_OBJECT (w), "clicked",
985 GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDYES));
986 GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
987 gtk_widget_grab_default (w);
990 w = gtk_button_new_with_label (_("No"));
991 gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
992 gtk_signal_connect (GTK_OBJECT (w), "clicked",
993 GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDNO));
1000 w = gtk_button_new_with_label (_("Go to URL"));
1001 gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
1002 gtk_signal_connect (GTK_OBJECT (w), "clicked",
1003 GTK_SIGNAL_FUNC (dialog_url_callback), NULL);
1004 g_object_set_data (G_OBJECT (w), "URL", (void *)URL);
1005 GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
1006 gtk_widget_grab_default (w);
1007 gtk_widget_show (w);
1011 gtk_widget_show (window);
1012 gtk_grab_add (window);
1015 gtk_main_iteration ();
1017 gtk_grab_remove (window);
1018 gtk_widget_destroy (window);
1023 // =============================================================================
1026 // fenris #3078 WHENHELLISFROZENOVER
1028 //#define FILEDLG_DBG
1030 static void file_sel_callback (GtkWidget *widget, gpointer data)
1036 parent = gtk_widget_get_toplevel (widget);
1037 loop = (int*)g_object_get_data (G_OBJECT (parent), "loop");
1038 success = (bool*)g_object_get_data (G_OBJECT (parent), "success");
1040 if (GPOINTER_TO_INT (data) == IDOK)
1045 Sys_Printf("file_sel_callback != IDOK\n");
1052 #include <commdlg.h>
1053 static OPENFILENAME ofn; /* common dialog box structure */
1054 static char szDirName[MAX_PATH]; /* directory string */
1055 static char szFile[MAX_PATH]; /* filename string */
1056 static char szFileTitle[MAX_PATH]; /* file title string */
1057 static int i, cbString; /* integer count variables */
1058 static HANDLE hf; /* file handle */
1060 static char szFile[QER_MAX_NAMELEN];
1063 #define FILEDLG_CUSTOM_FILTER_LENGTH 64
1064 // to be used with the advanced file selector
1066 class CFileType : public IFileTypeList
1068 struct filetype_copy_t
1070 void operator=(const filetype_t& other)
1072 m_name = other.name;
1073 m_pattern = other.pattern;
1083 m_strWin32Filters = NULL;
1084 m_pstrGTKMasks = NULL;
1087 virtual ~CFileType()
1090 DestroyWin32Filters();
1094 void addType(filetype_t type)
1096 filetype_copy_t* newTypes = new filetype_copy_t [m_nTypes+1];
1099 for(int i=0; i<m_nTypes; i++)
1100 newTypes[i] = m_pTypes[i];
1103 m_pTypes = newTypes;
1104 m_pTypes[m_nTypes] = type;
1106 ConstructGTKMasks();
1107 ConstructWin32Filters();
1110 filetype_t GetTypeForWin32Filter(const char *filter) const
1112 for(int i=0; i<m_nTypes; i++)
1113 if(strcmp(m_pTypes[i].m_pattern.c_str(), filter)==0)
1114 return filetype_t(m_pTypes[i].m_name.c_str(), m_pTypes[i].m_pattern.c_str());
1115 return filetype_t();
1118 filetype_t GetTypeForGTKMask(const char *mask) const
1120 for(int i=0; i<m_nTypes; i++)
1121 if(strcmp(m_pstrGTKMasks[i],mask)==0)
1122 return filetype_t(m_pTypes[i].m_name.c_str(), m_pTypes[i].m_pattern.c_str());
1123 return filetype_t();
1131 filetype_t GetTypeForIndex(int index) const // Zero-based index.
1133 if (index >= 0 && index < m_nTypes)
1134 return filetype_t(m_pTypes[index].m_name.c_str(), m_pTypes[index].m_pattern.c_str());
1135 return filetype_t();
1138 char *m_strWin32Filters;
1139 char **m_pstrGTKMasks;
1142 filetype_copy_t *m_pTypes;
1144 void DestroyWin32Filters()
1146 delete[] m_strWin32Filters;
1149 void ConstructWin32Filters()
1155 DestroyWin32Filters();
1156 for(i=0; i<m_nTypes; i++)
1157 len = len + strlen(m_pTypes[i].m_name.c_str()) + strlen(m_pTypes[i].m_pattern.c_str())*2 + 5;
1158 m_strWin32Filters = new char[len+1]; // length + null char
1159 for(i=0, w = m_strWin32Filters; i<m_nTypes; i++)
1161 for(r = m_pTypes[i].m_name.c_str(); *r!='\0'; r++, w++)
1165 for(r = m_pTypes[i].m_pattern.c_str(); *r!='\0'; r++, w++)
1169 for(r = m_pTypes[i].m_pattern.c_str(); *r!='\0'; r++, w++)
1170 *w = (*r == ',') ? ';' : *r;
1173 m_strWin32Filters[len] = '\0';
1176 void DestroyGTKMasks()
1178 if(m_pstrGTKMasks != NULL)
1179 for(char **p = m_pstrGTKMasks; *p != NULL; p++)
1181 delete[] m_pstrGTKMasks;
1184 void ConstructGTKMasks()
1191 m_pstrGTKMasks = new char*[m_nTypes+1];
1192 for(i=0; i<m_nTypes; i++)
1194 len = strlen(m_pTypes[i].m_name.c_str()) + strlen(m_pTypes[i].m_pattern.c_str()) + 3;
1195 m_pstrGTKMasks[i] = new char[len+1]; // length + null char
1196 w = m_pstrGTKMasks[i];
1197 for(r = m_pTypes[i].m_name.c_str(); *r!='\0'; r++, w++)
1201 for(r = m_pTypes[i].m_pattern.c_str(); *r!='\0'; r++, w++)
1206 m_pstrGTKMasks[m_nTypes] = NULL;
1213 static int in_file_dialog = 0;
1220 } win32_native_file_dialog_comms_t;
1222 DWORD WINAPI win32_native_file_dialog_thread_func(LPVOID lpParam)
1224 win32_native_file_dialog_comms_t *fileDialogComms;
1225 fileDialogComms = (win32_native_file_dialog_comms_t *) lpParam;
1226 if (fileDialogComms->open) {
1227 fileDialogComms->dlgRtnVal = GetOpenFileName(fileDialogComms->ofn);
1230 fileDialogComms->dlgRtnVal = GetSaveFileName(fileDialogComms->ofn);
1232 fileDialogComms->done = -1; // No need to synchronize around lock.
1239 * @param[in] baseSubDir should have a trailing slash if not @c NULL
1241 const char* file_dialog (void *parent, gboolean open, const char* title, const char* path, const char* pattern, const char *baseSubDir)
1245 HANDLE fileDialogThreadHandle;
1246 win32_native_file_dialog_comms_t fileDialogComms;
1251 GtkWidget* file_sel;
1253 char *new_path = NULL;
1260 GetFileTypeRegistry()->getTypeList(pattern, &typelist);
1263 Sys_Printf("file_dialog: open = %d title = %s path = %s\n", open, title, path);
1266 Sys_Printf("Patterns:\n");
1267 char** p = typelist.m_pstrGTKMasks;
1269 Sys_Printf("%s\n", *p++);
1272 Sys_Printf("no patterns\n");
1276 if (g_PrefsDlg.m_bNativeGUI)
1279 Sys_Printf("Doing win32 file dialog...");
1281 // do that the native way
1283 if (in_file_dialog) return NULL; // Avoid recursive entry.
1285 /* Set the members of the OPENFILENAME structure. */
1286 // See http://msdn.microsoft.com/en-us/library/ms646839%28v=vs.85%29.aspx .
1287 memset(&ofn, 0, sizeof(ofn));
1288 ofn.lStructSize = sizeof(ofn);
1289 ofn.hwndOwner = (HWND)GDK_WINDOW_HWND (g_pParentWnd->m_pWidget->window);
1290 ofn.nFilterIndex = 1; // The index is 1-based, not 0-based. This basically says,
1291 // "select the first filter as default".
1294 ofn.lpstrFilter = typelist.m_strWin32Filters;
1298 // TODO: Would be a bit cleaner if we could extract this string from
1299 // GetFileTypeRegistry() instead of hardcoding it here.
1300 ofn.lpstrFilter = "all files\0*.*\0"; // Second '\0' will be added to end of string.
1303 ofn.lpstrFile = szFile;
1304 ofn.nMaxFile = sizeof(szFile);
1307 // szDirName: Radiant uses unix convention for paths internally
1308 // Win32 (of course) and Gtk (who would have thought) expect the '\\' convention
1309 // copy path, replacing dir separators as appropriate
1310 for(r=path, w=szDirName; *r!='\0'; r++)
1311 *w++ = (*r=='/') ? '\\' : *r;
1314 ofn.lpstrInitialDir = szDirName;
1316 ofn.lpstrTitle = title;
1317 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
1319 memset(&fileDialogComms, 0, sizeof(fileDialogComms));
1320 fileDialogComms.open = open;
1321 fileDialogComms.ofn = &ofn;
1323 fileDialogThreadHandle =
1324 CreateThread(NULL, // lpThreadAttributes
1325 0, // dwStackSize, default stack size
1326 win32_native_file_dialog_thread_func, // lpStartAddress, funcion to call
1327 &fileDialogComms, // lpParameter, argument to pass to function
1328 0, // dwCreationFlags
1329 NULL); // lpThreadId
1333 // Avoid blocking indefinitely. Another thread will set fileDialogComms->done to nonzero;
1334 // we don't want to be in an indefinite blocked state when this happens. We want to break
1335 // out of here eventually.
1336 while (gtk_events_pending()) {
1337 gtk_main_iteration();
1339 if (dialogDone) break;
1340 if (fileDialogComms.done) dialogDone = 1; // One more loop of gtk_main_iteration() to get things in sync.
1341 // Avoid tight infinte loop, add a small amount of sleep.
1344 // Make absolutely sure that the thread is finished before we call CloseHandle().
1345 WaitForSingleObject(fileDialogThreadHandle, INFINITE);
1346 CloseHandle(fileDialogThreadHandle);
1350 if (!fileDialogComms.dlgRtnVal) {
1351 return NULL; // Cancelled.
1355 type = typelist.GetTypeForIndex(ofn.nFilterIndex - 1);
1358 Sys_Printf("Done.\n");
1365 // do that the Gtk way
1367 title = open ? _("Open File") : _("Save File");
1370 Sys_Printf("Doing Gtk file dialog:\nBuilding new_path..");
1372 // we expect an actual path below, if the path is NULL we might crash
1373 if (!path || path[0] == '\0')
1375 strcpy(buf, g_pGameDescription->mEnginePath.GetBuffer());
1376 strcat(buf, g_pGameDescription->mBaseGame.GetBuffer());
1379 strcat(buf, baseSubDir);
1383 // alloc new path with extra char for dir separator
1384 new_path = new char[strlen(path)+1+1];
1385 // copy path, replacing dir separators as appropriate
1386 for(r=path, w=new_path; *r!='\0'; r++)
1387 *w++ = (*r=='/') ? G_DIR_SEPARATOR : *r;
1388 // add dir separator to end of path if required
1389 if(*(w-1) != G_DIR_SEPARATOR) *w++ = G_DIR_SEPARATOR;
1394 Sys_Printf("Done.\n");
1395 Sys_Printf("Calling gtk_file_selection_new with title: %s...", title);
1397 file_sel = gtk_file_selection_new (title);
1399 Sys_Printf("Done.\n");
1400 Sys_Printf("Set the masks...");
1403 #if 0 //!\todo Add masks to GtkFileSelection in gtk-2.0
1407 gtk_file_selection_clear_masks (GTK_FILE_SELECTION (file_sel));
1408 gtk_file_selection_set_masks (GTK_FILE_SELECTION (file_sel), const_cast<const char**>(typelist.m_pstrGTKMasks));
1413 Sys_Printf("Done.\n");
1416 gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (file_sel)->ok_button), "clicked",
1417 GTK_SIGNAL_FUNC (file_sel_callback), GINT_TO_POINTER (IDOK));
1418 gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (file_sel)->cancel_button), "clicked",
1419 GTK_SIGNAL_FUNC (file_sel_callback), GINT_TO_POINTER (IDCANCEL));
1420 gtk_signal_connect (GTK_OBJECT (file_sel), "delete_event",
1421 GTK_SIGNAL_FUNC (dialog_delete_callback), NULL);
1422 gtk_file_selection_hide_fileop_buttons (GTK_FILE_SELECTION (file_sel));
1425 gtk_window_set_transient_for (GTK_WINDOW (file_sel), GTK_WINDOW (parent));
1428 Sys_Printf("set_data...");
1430 bool success = false;
1431 g_object_set_data (G_OBJECT (file_sel), "loop", &loop);
1432 g_object_set_data (G_OBJECT (file_sel), "success", &success);
1434 Sys_Printf("Done.\n");
1440 Sys_Printf("set_data \"overwrite\" ...");
1442 g_object_set_data (G_OBJECT (file_sel), "overwrite", GINT_TO_POINTER (1));
1444 Sys_Printf("Done.\n");
1448 if (new_path != NULL)
1451 Sys_Printf("gtk_file_selection_set_filename... %p (%s)", file_sel, new_path);
1453 gtk_file_selection_set_filename (GTK_FILE_SELECTION (file_sel), new_path);
1456 Sys_Printf("Done.\n");
1460 gtk_grab_add (file_sel);
1462 Sys_Printf("gtk_widget_show... %p", file_sel);
1464 gtk_widget_show (file_sel);
1466 Sys_Printf("Done.\n");
1470 Sys_Printf("gtk_main_iteration...");
1473 gtk_main_iteration ();
1476 #if 0 //!\todo Add masks to GtkFileSelection in gtk2
1478 type = typelist.GetTypeForGTKMask(GTK_FILE_SELECTION (file_sel)->mask);
1480 strcpy(szFile, gtk_file_selection_get_filename (GTK_FILE_SELECTION (file_sel)));
1483 Sys_Printf("Done.\n");
1486 gtk_grab_remove (file_sel);
1487 gtk_widget_destroy (file_sel);
1492 // don't return an empty filename
1493 if(szFile[0] == '\0') return NULL;
1495 // convert back to unix format
1496 for(w=szFile; *w!='\0'; w++)
1501 if (g_PrefsDlg.m_bNativeGUI)
1503 /* \todo SPoG - file_dialog should return filetype information separately.. not force file extension.. */
1504 if(!open && pattern != NULL)
1506 // last ext separator
1507 w = strrchr(szFile, '.');
1508 if (w == NULL) { // No extension.
1509 w = szFile + strlen(szFile);
1510 strcpy(w, type.pattern + 1); // Add extension of selected filter type.
1512 else { // An extension was explicitly in the filename.
1513 int knownExtension = 0;
1514 for (int i = typelist.GetNumTypes() - 1; i >= 0; i--) {
1515 type = typelist.GetTypeForIndex(i);
1516 if (stricmp(w, type.pattern + 1) == 0) {
1521 if (!knownExtension) {
1522 if (gtk_MessageBox(parent, "Unknown file extension for this save operation.\nAttempt to save anyways?",
1523 "GtkRadiant", MB_YESNO) == IDNO) {
1532 // prompt to overwrite existing files
1534 if (access (szFile, R_OK) == 0)
1535 if (gtk_MessageBox (parent, "File already exists.\nOverwrite?", "GtkRadiant", MB_YESNO) == IDNO)
1539 // ... let's use a static filename
1540 Sys_Printf("filename: %p\n", szFile);
1546 char* WINAPI dir_dialog (void *parent, const char* title, const char* path)
1548 GtkWidget* file_sel;
1549 char* filename = (char*)NULL;
1551 bool success = false;
1553 file_sel = gtk_file_selection_new (title);
1554 gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (file_sel)->ok_button), "clicked",
1555 GTK_SIGNAL_FUNC (file_sel_callback), GINT_TO_POINTER (IDOK));
1556 gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (file_sel)->cancel_button), "clicked",
1557 GTK_SIGNAL_FUNC (file_sel_callback), GINT_TO_POINTER (IDCANCEL));
1558 gtk_signal_connect (GTK_OBJECT (file_sel), "delete_event",
1559 GTK_SIGNAL_FUNC (dialog_delete_callback), NULL);
1560 gtk_file_selection_hide_fileop_buttons (GTK_FILE_SELECTION (file_sel));
1562 if ( parent != NULL ) {
1563 gtk_window_set_transient_for( GTK_WINDOW( file_sel ), GTK_WINDOW( parent ) );
1566 gtk_widget_hide (GTK_FILE_SELECTION (file_sel)->file_list->parent);
1568 g_object_set_data (G_OBJECT (file_sel), "loop", &loop);
1569 g_object_set_data (G_OBJECT (file_sel), "success", &success);
1572 gtk_file_selection_set_filename (GTK_FILE_SELECTION (file_sel), path);
1574 gtk_grab_add (file_sel);
1575 gtk_widget_show (file_sel);
1578 gtk_main_iteration ();
1580 filename = g_strdup( gtk_file_selection_get_filename( GTK_FILE_SELECTION( file_sel ) ) );
1582 gtk_grab_remove( file_sel );
1583 gtk_widget_destroy( file_sel );
1588 bool WINAPI color_dialog (void *parent, float *color, const char* title)
1592 int loop = 1, ret = IDCANCEL;
1598 dlg = gtk_color_selection_dialog_new (title);
1599 gtk_color_selection_set_color (GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG (dlg)->colorsel), clr);
1600 gtk_signal_connect (GTK_OBJECT (dlg), "delete_event",
1601 GTK_SIGNAL_FUNC (dialog_delete_callback), NULL);
1602 gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
1603 GTK_SIGNAL_FUNC (gtk_widget_destroy), NULL);
1604 gtk_signal_connect (GTK_OBJECT (GTK_COLOR_SELECTION_DIALOG (dlg)->ok_button), "clicked",
1605 GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDOK));
1606 gtk_signal_connect (GTK_OBJECT (GTK_COLOR_SELECTION_DIALOG (dlg)->cancel_button), "clicked",
1607 GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDCANCEL));
1608 g_object_set_data (G_OBJECT (dlg), "loop", &loop);
1609 g_object_set_data (G_OBJECT (dlg), "ret", &ret);
1612 gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (parent));
1614 gtk_widget_show (dlg);
1618 gtk_main_iteration ();
1621 gtk_color_selection_get_current_color (GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG (dlg)->colorsel), &gdkcolor);
1622 clr[0] = gdkcolor.red / 65535.0;
1623 clr[1] = gdkcolor.green / 65535.0;
1624 clr[2] = gdkcolor.blue / 65535.0;
1626 gtk_grab_remove (dlg);
1627 gtk_widget_destroy (dlg);
1631 color[0] = (float)clr[0];
1632 color[1] = (float)clr[1];
1633 color[2] = (float)clr[2];
1641 void OpenURL(const char *url)
1643 // let's put a little comment
1644 Sys_Printf("OpenURL: %s\n", url);
1646 // \todo FIXME: the way we open URLs on *nix should be improved. A script is good (see how I do on RTCW)
1647 char command[2*PATH_MAX];
1648 snprintf( command, sizeof(command), "%s/openurl.sh \"%s\" &", g_strAppPath.GetBuffer(), url );
1649 if (system (command) != 0)
1650 gtk_MessageBox (g_pParentWnd->m_pWidget, "Failed to launch Netscape!");
1653 char command[2*PATH_MAX];
1654 snprintf (command, sizeof(command),
1655 "open \"%s\" &", url, url);
1656 if (system (command) != 0)
1657 gtk_MessageBox (g_pParentWnd->m_pWidget, "Unable to launch browser!");
1660 ShellExecute( (HWND)GDK_WINDOW_HWND (g_pParentWnd->m_pWidget->window), "open", url, NULL, NULL, SW_SHOW );
1664 void CheckMenuSplitting (GtkWidget *&menu)
1666 GtkWidget *item,*menu2;
1668 GtkRequisition requisition;
1671 gtk_widget_size_request (GTK_WIDGET (menu), &requisition);
1672 screen_height = gdk_screen_height ();
1674 if ((screen_height - requisition.height) < 20)
1676 menu2 = gtk_menu_new ();
1678 // move the last 2 items to a submenu (3 because of win32)
1679 for (int i = 0; i < 3; i++)
1681 item = GTK_WIDGET (g_list_last (gtk_container_children (GTK_CONTAINER (menu)))->data);
1682 gtk_widget_ref (item);
1683 gtk_container_remove (GTK_CONTAINER (menu), item);
1684 gtk_menu_append (GTK_MENU (menu2), item);
1685 gtk_widget_unref (item);
1688 item = gtk_menu_item_new_with_label ("--------");
1689 gtk_widget_show (item);
1690 gtk_container_add (GTK_CONTAINER (menu), item);
1691 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu2);