//
#include <gdk/gdkkeysyms.h>
+#include <glib/gi18n.h>
#if defined (__linux__) || defined (__APPLE__)
#include <unistd.h>
#ifdef _WIN32
void win32_get_window_pos(GtkWidget *widget, gint *x, gint *y)
{
- // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=913
if ( g_PrefsDlg.m_bStartOnPrimMon ) {
RECT rc;
POINT point;
ret = (int*)g_object_get_data( G_OBJECT( parent ), "ret" );
*loop = 0;
- *ret = (int)data;
+ *ret = GPOINTER_TO_INT (data);
}
gint dialog_delete_callback (GtkWidget *widget, GdkEvent* event, gpointer data)
if (mode == MB_OK)
{
- w = gtk_button_new_with_label ("Ok");
+ w = gtk_button_new_with_label (_("Ok"));
gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT (w), "clicked",
GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDOK));
}
else if (mode == MB_OKCANCEL)
{
- w = gtk_button_new_with_label ("Ok");
+ w = gtk_button_new_with_label (_("Ok"));
gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT (w), "clicked",
GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDOK));
gtk_widget_grab_default (w);
gtk_widget_show (w);
- w = gtk_button_new_with_label ("Cancel");
+ w = gtk_button_new_with_label (_("Cancel"));
gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT (w), "clicked",
GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDCANCEL));
}
else if (mode == MB_YESNOCANCEL)
{
- w = gtk_button_new_with_label ("Yes");
+ w = gtk_button_new_with_label (_("Yes"));
gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT (w), "clicked",
GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDYES));
gtk_widget_grab_default (w);
gtk_widget_show (w);
- w = gtk_button_new_with_label ("No");
+ w = gtk_button_new_with_label (_("No"));
gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT (w), "clicked",
GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDNO));
gtk_widget_show (w);
- w = gtk_button_new_with_label ("Cancel");
+ w = gtk_button_new_with_label (_("Cancel"));
gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT (w), "clicked",
GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDCANCEL));
}
else /* if (mode == MB_YESNO) */
{
- w = gtk_button_new_with_label ("Yes");
+ w = gtk_button_new_with_label (_("Yes"));
gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT (w), "clicked",
GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDYES));
gtk_widget_grab_default (w);
gtk_widget_show (w);
- w = gtk_button_new_with_label ("No");
+ w = gtk_button_new_with_label (_("No"));
gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT (w), "clicked",
GTK_SIGNAL_FUNC (dialog_button_callback), GINT_TO_POINTER (IDNO));
if (URL)
{
- w = gtk_button_new_with_label ("Go to URL");
+ w = gtk_button_new_with_label (_("Go to URL"));
gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT (w), "clicked",
GTK_SIGNAL_FUNC (dialog_url_callback), NULL);
loop = (int*)g_object_get_data (G_OBJECT (parent), "loop");
success = (bool*)g_object_get_data (G_OBJECT (parent), "success");
- if ((int)data == IDOK)
+ if (GPOINTER_TO_INT (data) == IDOK)
*success = true;
#ifdef FILEDLG_DBG
return filetype_t();
}
+ int GetNumTypes()
+ {
+ return m_nTypes;
+ }
+
+ filetype_t GetTypeForIndex(int index) const // Zero-based index.
+ {
+ if (index >= 0 && index < m_nTypes)
+ return filetype_t(m_pTypes[index].m_name.c_str(), m_pTypes[index].m_pattern.c_str());
+ return filetype_t();
+ }
+
char *m_strWin32Filters;
char **m_pstrGTKMasks;
private:
};
-const char* file_dialog (void *parent, gboolean open, const char* title, const char* path, const char* pattern)
+#ifdef _WIN32
+
+static int in_file_dialog = 0;
+
+typedef struct {
+ gboolean open;
+ OPENFILENAME *ofn;
+ BOOL dlgRtnVal;
+ int done;
+} win32_native_file_dialog_comms_t;
+
+DWORD WINAPI win32_native_file_dialog_thread_func(LPVOID lpParam)
+{
+ win32_native_file_dialog_comms_t *fileDialogComms;
+ fileDialogComms = (win32_native_file_dialog_comms_t *) lpParam;
+ if (fileDialogComms->open) {
+ fileDialogComms->dlgRtnVal = GetOpenFileName(fileDialogComms->ofn);
+ }
+ else {
+ fileDialogComms->dlgRtnVal = GetSaveFileName(fileDialogComms->ofn);
+ }
+ fileDialogComms->done = -1; // No need to synchronize around lock.
+ return 0;
+}
+
+#endif
+
+/**
+ * @param[in] baseSubDir should have a trailing slash if not @c NULL
+ */
+const char* file_dialog (void *parent, gboolean open, const char* title, const char* path, const char* pattern, const char *baseSubDir)
{
+
+#ifdef _WIN32
+ HANDLE fileDialogThreadHandle;
+ win32_native_file_dialog_comms_t fileDialogComms;
+ int dialogDone;
+#endif
+
// Gtk dialog
GtkWidget* file_sel;
int loop = 1;
#endif
#ifdef _WIN32
- // win32 dialog stores the selected "save as type" extension in the second null-terminated string
- char customfilter[FILEDLG_CUSTOM_FILTER_LENGTH];
-
if (g_PrefsDlg.m_bNativeGUI)
{
#ifdef FILEDLG_DBG
Sys_Printf("Doing win32 file dialog...");
#endif
// do that the native way
- /* Place the terminating null character in the szFile. */
- szFile[0] = '\0';
- customfilter[0] = customfilter[1] = customfilter[2] = '\0';
+ if (in_file_dialog) return NULL; // Avoid recursive entry.
+ in_file_dialog = 1;
/* Set the members of the OPENFILENAME structure. */
- ofn.lStructSize = sizeof(OPENFILENAME);
+ // See http://msdn.microsoft.com/en-us/library/ms646839%28v=vs.85%29.aspx .
+ memset(&ofn, 0, sizeof(ofn));
+ ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = (HWND)GDK_WINDOW_HWND (g_pParentWnd->m_pWidget->window);
+ ofn.nFilterIndex = 1; // The index is 1-based, not 0-based. This basically says,
+ // "select the first filter as default".
if (pattern)
{
- ofn.nFilterIndex = 0;
ofn.lpstrFilter = typelist.m_strWin32Filters;
}
- else ofn.nFilterIndex = 1;
- ofn.lpstrCustomFilter = customfilter;
- ofn.nMaxCustFilter = sizeof(customfilter);
+ else
+ {
+ // TODO: Would be a bit cleaner if we could extract this string from
+ // GetFileTypeRegistry() instead of hardcoding it here.
+ ofn.lpstrFilter = "all files\0*.*\0"; // Second '\0' will be added to end of string.
+ }
+ szFile[0] = '\0';
ofn.lpstrFile = szFile;
ofn.nMaxFile = sizeof(szFile);
- ofn.lpstrFileTitle = NULL; // we don't need to get the name of the file
if(path)
{
// szDirName: Radiant uses unix convention for paths internally
*w = '\0';
ofn.lpstrInitialDir = szDirName;
}
- else ofn.lpstrInitialDir = NULL;
ofn.lpstrTitle = title;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
- /* Display the Open dialog box. */
- // it's open or close depending on 'open' parameter
- if (open)
- {
- if (!GetOpenFileName(&ofn))
- return NULL; // canceled
+ memset(&fileDialogComms, 0, sizeof(fileDialogComms));
+ fileDialogComms.open = open;
+ fileDialogComms.ofn = &ofn;
+
+ fileDialogThreadHandle =
+ CreateThread(NULL, // lpThreadAttributes
+ 0, // dwStackSize, default stack size
+ win32_native_file_dialog_thread_func, // lpStartAddress, funcion to call
+ &fileDialogComms, // lpParameter, argument to pass to function
+ 0, // dwCreationFlags
+ NULL); // lpThreadId
+
+ dialogDone = 0;
+ while (1) {
+ // Avoid blocking indefinitely. Another thread will set fileDialogComms->done to nonzero;
+ // we don't want to be in an indefinite blocked state when this happens. We want to break
+ // out of here eventually.
+ while (gtk_events_pending()) {
+ gtk_main_iteration();
+ }
+ if (dialogDone) break;
+ if (fileDialogComms.done) dialogDone = 1; // One more loop of gtk_main_iteration() to get things in sync.
+ // Avoid tight infinte loop, add a small amount of sleep.
+ Sleep(10);
}
- else
- {
- if (!GetSaveFileName(&ofn))
- return NULL; // canceled
+ // Make absolutely sure that the thread is finished before we call CloseHandle().
+ WaitForSingleObject(fileDialogThreadHandle, INFINITE);
+ CloseHandle(fileDialogThreadHandle);
+
+ in_file_dialog = 0;
+
+ if (!fileDialogComms.dlgRtnVal) {
+ return NULL; // Cancelled.
}
if(pattern != NULL)
- type = typelist.GetTypeForWin32Filter(customfilter+1);
+ type = typelist.GetTypeForIndex(ofn.nFilterIndex - 1);
#ifdef FILEDLG_DBG
Sys_Printf("Done.\n");
else
{
#endif
+ char buf[PATH_MAX];
// do that the Gtk way
if (title == NULL)
- title = open ? "Open File" : "Save File";
+ title = open ? _("Open File") : _("Save File");
#ifdef FILEDLG_DBG
Sys_Printf("Doing Gtk file dialog:\nBuilding new_path..");
// we expect an actual path below, if the path is NULL we might crash
if (!path || path[0] == '\0')
{
-#ifdef _WIN32
- path = "C:\\";
-#elif defined (__linux__) || defined (__APPLE__)
- path = "/";
-#else
- path = "/";
-#endif
- }
+ strcpy(buf, g_pGameDescription->mEnginePath.GetBuffer());
+ strcat(buf, g_pGameDescription->mBaseGame.GetBuffer());
+ strcat(buf, "/");
+ if (baseSubDir)
+ strcat(buf, baseSubDir);
+ path = buf;
+ }
// alloc new path with extra char for dir separator
new_path = new char[strlen(path)+1+1];
*w = '\0';
#ifdef FILEDLG_DBG
- Sys_Printf("Done.\n");
- Sys_Printf("Calling gtk_file_selection_new with title: %s...", title);
+ Sys_Printf("Done.\n");
+ Sys_Printf("Calling gtk_file_selection_new with title: %s...", title);
#endif
-
file_sel = gtk_file_selection_new (title);
-
#ifdef FILEDLG_DBG
- Sys_Printf("Done.\n");
- Sys_Printf("Set the masks...");
+ Sys_Printf("Done.\n");
+ Sys_Printf("Set the masks...");
#endif
#if 0 //!\todo Add masks to GtkFileSelection in gtk-2.0
if (new_path != NULL)
{
#ifdef FILEDLG_DBG
- Sys_Printf("gtk_file_selection_set_filename... %p", file_sel);
+ Sys_Printf("gtk_file_selection_set_filename... %p (%s)", file_sel, new_path);
#endif
gtk_file_selection_set_filename (GTK_FILE_SELECTION (file_sel), new_path);
delete[] new_path;
*w = '/';
#if defined(WIN32)
- if (g_PrefsDlg.m_bNativeGUI) // filetype mask not supported in gtk dialog yet
+ if (g_PrefsDlg.m_bNativeGUI)
{
- // when saving, force an extension depending on filetype
/* \todo SPoG - file_dialog should return filetype information separately.. not force file extension.. */
if(!open && pattern != NULL)
{
// last ext separator
w = strrchr(szFile, '.');
- // no extension
- w = (w!=NULL) ? w : szFile+strlen(szFile);
- strcpy(w, type.pattern+1);
+ if (w == NULL) { // No extension.
+ w = szFile + strlen(szFile);
+ strcpy(w, type.pattern + 1); // Add extension of selected filter type.
+ }
+ else { // An extension was explicitly in the filename.
+ int knownExtension = 0;
+ for (int i = typelist.GetNumTypes() - 1; i >= 0; i--) {
+ type = typelist.GetTypeForIndex(i);
+ if (stricmp(w, type.pattern + 1) == 0) {
+ knownExtension = 1;
+ break;
+ }
+ }
+ if (!knownExtension) {
+ if (gtk_MessageBox(parent, "Unknown file extension for this save operation.\nAttempt to save anyways?",
+ "GtkRadiant", MB_YESNO) == IDNO) {
+ return NULL;
+ }
+ }
+ }
}
}
#endif
GTK_SIGNAL_FUNC (dialog_delete_callback), NULL);
gtk_file_selection_hide_fileop_buttons (GTK_FILE_SELECTION (file_sel));
- if (parent != NULL)
- gtk_window_set_transient_for (GTK_WINDOW (file_sel), GTK_WINDOW (parent));
+ if ( parent != NULL ) {
+ gtk_window_set_transient_for( GTK_WINDOW( file_sel ), GTK_WINDOW( parent ) );
+ }
gtk_widget_hide (GTK_FILE_SELECTION (file_sel)->file_list->parent);
while (loop)
gtk_main_iteration ();
- filename = g_strdup(gtk_file_selection_get_filename (GTK_FILE_SELECTION (file_sel)));
+ filename = g_strdup( gtk_file_selection_get_filename( GTK_FILE_SELECTION( file_sel ) ) );
- gtk_grab_remove (file_sel);
- gtk_widget_destroy (file_sel);
+ gtk_grab_remove( file_sel );
+ gtk_widget_destroy( file_sel );
return filename;
}