Index: addons/native_dialog/win_dialog.c
===================================================================
--- addons/native_dialog/win_dialog.c	(revision 14289)
+++ addons/native_dialog/win_dialog.c	(working copy)
@@ -19,6 +19,7 @@
 
 /* We use RichEdit by default. */
 #include <richedit.h>
+#include <shlobj.h> // for folder selector
 
 ALLEGRO_DEBUG_CHANNEL("win_dialog")
 
@@ -46,95 +47,218 @@
    return i+1;
 }
 
+/* count the number of patterns */
+static unsigned int count_patterns(ALLEGRO_USTR * patterns) {
+   const char * cstr;
+   int count = 1;
+   unsigned int i;
+   
+   cstr = al_cstr(patterns);
+   for (i = 0; i < al_ustr_size(patterns); i++) {
+      if (cstr[i] == ';') {
+         count++;
+      }
+   }
+   return count;
+}
+
+static char get_pattern_return[256];
+
+/* gets the extension from the list */
+static const char * get_pattern(ALLEGRO_USTR * patterns, int index) {
+   const char * cstr;
+   int pattern_pos = 0;
+   int pattern_num = 0;
+   unsigned int i;
+   
+   cstr = al_cstr(patterns);
+   for (i = 0; i < al_ustr_size(patterns); i++) {
+      if (cstr[i] == ';') {
+         pattern_num++;
+         if (pattern_num > index)
+            return get_pattern_return;
+         else
+            pattern_pos = 0;
+      }
+      else {
+         get_pattern_return[pattern_pos] = cstr[i];
+         pattern_pos++;
+         get_pattern_return[pattern_pos] = '\0';
+      }
+   }
+   return get_pattern_return;
+}
+
 bool _al_show_native_file_dialog(ALLEGRO_DISPLAY *display,
    ALLEGRO_NATIVE_DIALOG *fd)
 {
    OPENFILENAME ofn;
+   BROWSEINFO folderinfo; // for folder selector
+   LPCITEMIDLIST pidl;    // for folder selector
    ALLEGRO_DISPLAY_WIN *win_display;
    int flags = 0;
    bool ret;
    char buf[4096] = "";
+   char dbuf[MAX_PATH] = "";
    char init_dir[4096] = "";
-   int i;
+   char filter[4096] = "";
+   int filter_pos = 0;
+   bool first_filter = true;
+   bool filter_all = false; // set to true if user has *.* in patterns
+   const char * filter_ext = NULL;
+   unsigned int i, j;
 
-   memset(&ofn, 0, sizeof(OPENFILENAME));
-
-   ofn.lStructSize = sizeof(OPENFILENAME);
    win_display = (ALLEGRO_DISPLAY_WIN *)display;
-   ofn.hwndOwner = (win_display) ? win_display->window : NULL;
+   
+   /* select a folder */
+   if (fd->flags & ALLEGRO_FILECHOOSER_FOLDER) {
+      folderinfo.hwndOwner = win_display->window;
+      folderinfo.pidlRoot = NULL;
+      folderinfo.pszDisplayName = dbuf;
+      folderinfo.lpszTitle = al_cstr(fd->title);
+      folderinfo.ulFlags = 0;
+      folderinfo.lpfn = NULL;
 
-   ofn.lpstrFile = buf;
-   ofn.nMaxFile = sizeof(buf);
-
-   if (fd->fc_initial_path) {
-      _al_sane_strncpy(init_dir,
-         al_path_cstr(fd->fc_initial_path, ALLEGRO_NATIVE_PATH_SEP),
-         sizeof(init_dir));
-      ofn.lpstrInitialDir = init_dir;
+      pidl = SHBrowseForFolder(&folderinfo);
+      if (pidl) {
+         SHGetPathFromIDList(pidl, buf);
+         fd->fc_path_count = 1;
+         fd->fc_paths = al_malloc(sizeof(void *));
+         fd->fc_paths[0] = al_create_path(buf);
+         return true;
+      }
+      return false;
    }
-
-   flags |= OFN_NOCHANGEDIR | OFN_EXPLORER;
-   if (fd->flags & ALLEGRO_FILECHOOSER_SAVE) {
-      flags |= OFN_OVERWRITEPROMPT;
-   }
+   
+   /* select a file */
    else {
-      flags |= (fd->flags & ALLEGRO_FILECHOOSER_FILE_MUST_EXIST) ? OFN_FILEMUSTEXIST : 0;
-   }
-   flags |= (fd->flags & ALLEGRO_FILECHOOSER_MULTIPLE) ? OFN_ALLOWMULTISELECT : 0;
-   flags |= (fd->flags & ALLEGRO_FILECHOOSER_SHOW_HIDDEN) ? 0x10000000 : 0; // NOTE: 0x10000000 is FORCESHOWHIDDEN
-   ofn.Flags = flags;
 
-   if (flags & OFN_OVERWRITEPROMPT) {
-      ret = GetSaveFileName(&ofn);
-   }
-   else {
-      ret = GetOpenFileName(&ofn);
-   }
+      memset(&ofn, 0, sizeof(OPENFILENAME));
 
-   if (!ret) {
-      DWORD err = GetLastError();
-      if (err != ERROR_SUCCESS) {
-         char buf[1000];
-         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, buf, sizeof(buf), NULL);
-         ALLEGRO_ERROR("al_show_native_file_dialog failed: %s\n", buf);
+      ofn.lStructSize = sizeof(OPENFILENAME);
+      ofn.hwndOwner = (win_display) ? win_display->window : NULL;
+
+      /* create filter string */
+      if (fd->fc_patterns) {
+         strncpy(filter, "All Supported Files", 4096);
+         filter_pos = 19 + 1;
+         for (i = 0; i < count_patterns(fd->fc_patterns); i++) {
+            filter_ext = get_pattern(fd->fc_patterns, i);
+            if (strncmp(filter_ext, "*.*", 256)) { // exclude *.*, it will be added separately
+               /* add a semicolon before pattern if it isn't the first pattern */
+               if(!first_filter) {
+                  filter[filter_pos] = ';';
+                  filter_pos++;
+               }
+               else {
+                  first_filter = false;
+               }
+               for (j = 0; j < strlen(filter_ext); j++) {
+                  filter[filter_pos] = filter_ext[j];
+                  filter_pos++;
+               }
+            }
+            else {
+               filter_all = true;
+            }
+         }
+         
+         /* if *.* is in the patterns add a separate "All Files" filter */
+         if (filter_all) {
+            filter[filter_pos] = '\0';
+            filter_pos++;
+            memcpy(&filter[filter_pos], "All Files", strlen("All Files") + 1);
+            filter_pos += strlen("All Files") + 1;
+            memcpy(&filter[filter_pos], "*.*", strlen("*.*") + 1);
+            filter_pos += strlen("*.*") + 1;
+         }
+         
+         /* close out the filter string (needs two NULL characters) */
+         filter[filter_pos] = '\0';
+         filter_pos++;
+         filter[filter_pos] = '\0';
+               
+         ofn.lpstrFilter = filter;
       }
-      return false;
-   }
+      else
+         ofn.lpstrFilter = "All Files\0*.*\0\0"; // list all files by default
 
-   if (flags & OFN_ALLOWMULTISELECT) {
-      int i;
-      /* Count number of file names in buf. */
-      fd->fc_path_count = 0;
-      i = skip_nul_terminated_string(buf);
-      while (1) {
-         if (buf[i] == '\0') {
-            fd->fc_path_count++;
-            if (buf[i+1] == '\0')
-               break;
+      ofn.lpstrFile = buf;
+      ofn.nMaxFile = sizeof(buf);
+
+      if (fd->fc_initial_path) {
+         _al_sane_strncpy(init_dir,
+            al_path_cstr(fd->fc_initial_path, ALLEGRO_NATIVE_PATH_SEP),
+            sizeof(init_dir));
+         ofn.lpstrInitialDir = init_dir;
+      }
+      
+      if (fd->title)
+         ofn.lpstrTitle = al_cstr(fd->title);
+
+      flags |= OFN_NOCHANGEDIR | OFN_EXPLORER;
+      if (fd->flags & ALLEGRO_FILECHOOSER_SAVE) {
+         flags |= OFN_OVERWRITEPROMPT;
+      }
+      else {
+         flags |= (fd->flags & ALLEGRO_FILECHOOSER_FILE_MUST_EXIST) ? OFN_FILEMUSTEXIST : 0;
+      }
+      flags |= (fd->flags & ALLEGRO_FILECHOOSER_MULTIPLE) ? OFN_ALLOWMULTISELECT : 0;
+      flags |= (fd->flags & ALLEGRO_FILECHOOSER_SHOW_HIDDEN) ? 0x10000000 : 0; // NOTE: 0x10000000 is FORCESHOWHIDDEN
+      ofn.Flags = flags;
+
+      if (flags & OFN_OVERWRITEPROMPT) {
+         ret = GetSaveFileName(&ofn);
+      }
+      else {
+         ret = GetOpenFileName(&ofn);
+      }
+
+      if (!ret) {
+         DWORD err = GetLastError();
+         if (err != ERROR_SUCCESS) {
+            char buf[1000];
+            FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, buf, sizeof(buf), NULL);
+            ALLEGRO_ERROR("al_show_native_file_dialog failed: %s\n", buf);
          }
-         i++;
+         return false;
       }
-   }
-   else {
-      fd->fc_path_count = 1;
-   }
 
-   if (fd->fc_path_count == 1) {
-      fd->fc_paths = al_malloc(sizeof(void *));
-      fd->fc_paths[0] = al_create_path(buf);
-   }
-   else {
-      int p;
-      /* If multiple files were selected, the first string in buf is the
-       * directory name, followed by each of the file names terminated by NUL.
-       */
-      fd->fc_paths = al_malloc(fd->fc_path_count * sizeof(void *));
-      i = skip_nul_terminated_string(buf);
-      for (p = 0; p < (int)fd->fc_path_count; p++) {
-         fd->fc_paths[p] = al_create_path_for_directory(buf);
-         al_set_path_filename(fd->fc_paths[p], buf+i);
-         i += skip_nul_terminated_string(buf+i);
+      if (flags & OFN_ALLOWMULTISELECT) {
+         int i;
+         /* Count number of file names in buf. */
+         fd->fc_path_count = 0;
+         i = skip_nul_terminated_string(buf);
+         while (1) {
+            if (buf[i] == '\0') {
+               fd->fc_path_count++;
+               if (buf[i+1] == '\0')
+                  break;
+            }
+            i++;
+         }
       }
+      else {
+         fd->fc_path_count = 1;
+      }
+
+      if (fd->fc_path_count == 1) {
+         fd->fc_paths = al_malloc(sizeof(void *));
+         fd->fc_paths[0] = al_create_path(buf);
+      }
+      else {
+         int p;
+         /* If multiple files were selected, the first string in buf is the
+          * directory name, followed by each of the file names terminated by NUL.
+          */
+         fd->fc_paths = al_malloc(fd->fc_path_count * sizeof(void *));
+         i = skip_nul_terminated_string(buf);
+         for (p = 0; p < (int)fd->fc_path_count; p++) {
+            fd->fc_paths[p] = al_create_path_for_directory(buf);
+            al_set_path_filename(fd->fc_paths[p], buf+i);
+            i += skip_nul_terminated_string(buf+i);
+         }
+      }
    }
 
    return true;
