]> git.xonotic.org Git - xonotic/netradiant.git/blobdiff - radiant/gtkmisc.cpp
Another Windows file dialog change in Rambetter-temp-fixes branch.
[xonotic/netradiant.git] / radiant / gtkmisc.cpp
index 63d5bf75a82e1b77538d411c6bd4ff63e4176b6f..f398604b8cf18fbe3280196245f3ef7ffb55913e 100644 (file)
@@ -859,7 +859,7 @@ void dialog_button_callback( GtkWidget *widget, gpointer data ) {
   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)
@@ -1037,7 +1037,7 @@ static void file_sel_callback (GtkWidget *widget, gpointer data)
   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
@@ -1123,6 +1123,18 @@ public:
     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:
@@ -1196,11 +1208,45 @@ private:
 
 };
 
+#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;
@@ -1227,33 +1273,35 @@ const char* file_dialog (void *parent, gboolean open, const char* title, const c
 #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
@@ -1265,25 +1313,46 @@ const char* file_dialog (void *parent, gboolean open, const char* title, const c
       *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");
@@ -1429,17 +1498,33 @@ const char* file_dialog (void *parent, gboolean open, const char* title, const c
       *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
@@ -1474,8 +1559,9 @@ char* WINAPI dir_dialog (void *parent, const char* title, const char* path)
                       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);
 
@@ -1491,10 +1577,10 @@ char* WINAPI dir_dialog (void *parent, const char* title, const char* path)
   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;
 }