home *** CD-ROM | disk | FTP | other *** search
/ Computer Shopper 275 / DPCS0111DVD.ISO / Toolkit / Audio-Visual / VirtualDub / Source / VirtualDub-1.9.10-src.7z / src / Dita / source / services.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2009-09-14  |  21.1 KB  |  765 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    Copyright (C) 1998-2004 Avery Lee
  3. //
  4. //    This program is free software; you can redistribute it and/or modify
  5. //    it under the terms of the GNU General Public License as published by
  6. //    the Free Software Foundation; either version 2 of the License, or
  7. //    (at your option) any later version.
  8. //
  9. //    This program is distributed in the hope that it will be useful,
  10. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. //    GNU General Public License for more details.
  13. //
  14. //    You should have received a copy of the GNU General Public License
  15. //    along with this program; if not, write to the Free Software
  16. //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. #include <stdafx.h>
  19. #include <map>
  20. #include <vector>
  21. #include <string.h>
  22.  
  23. #define _WIN32_WINNT 0x0500
  24.  
  25. #include <windows.h>
  26. #include <commdlg.h>
  27. #include <objbase.h>
  28. #include <shlobj.h>
  29.  
  30. #include <vd2/system/filesys.h>
  31. #include <vd2/system/strutil.h>
  32. #include <vd2/system/text.h>
  33. #include <vd2/system/registry.h>
  34. #include <vd2/system/thread.h>
  35. #include <vd2/system/vdalloc.h>
  36. #include <vd2/system/VDString.h>
  37. #include <vd2/system/w32assist.h>
  38. #include <vd2/Dita/services.h>
  39.  
  40. #ifndef OPENFILENAME_SIZE_VERSION_400
  41. #define OPENFILENAME_SIZE_VERSION_400 0x4c
  42. #endif
  43.  
  44. #ifndef BIF_NEWDIALOGSTYLE
  45. #define BIF_NEWDIALOGSTYLE     0x0040
  46. #endif
  47.  
  48. ///////////////////////////////////////////////////////////////////////////
  49.  
  50. #if 0
  51. IVDUIContext *VDGetUIContext() {
  52.     static vdautoptr<IVDUIContext> spctx(VDCreateUIContext());
  53.  
  54.     return spctx;
  55. }
  56. #endif
  57.  
  58. ///////////////////////////////////////////////////////////////////////////
  59.  
  60. struct FilespecEntry {
  61.     wchar_t szFile[MAX_PATH];
  62. };
  63.  
  64. typedef std::map<long, FilespecEntry> tFilespecMap;
  65.  
  66. // Visual C++ 7.0 has a bug with lock initialization in the STL -- the lock
  67. // code uses a normal constructor, and thus usually executes too late for
  68. // static construction.
  69. tFilespecMap *g_pFilespecMap;
  70. VDCriticalSection g_csFilespecMap;
  71.  
  72. ///////////////////////////////////////////////////////////////////////////
  73.  
  74. namespace {
  75.     int FileFilterLength(const wchar_t *pszFilter) {
  76.         const wchar_t *s = pszFilter;
  77.  
  78.         while(*s) {
  79.             while(*s++);
  80.             while(*s++);
  81.         }
  82.  
  83.         return s - pszFilter;
  84.     }
  85.  
  86. #pragma pack(push, 1)
  87.     struct DialogTemplateHeader {        // DLGTEMPLATEEX psuedo-struct from MSDN
  88.         WORD        signature;            // DOCBUG: This comes first!
  89.         WORD        dlgVer;
  90.         DWORD        helpID;
  91.         DWORD        exStyle;
  92.         DWORD        style;
  93.         WORD        cDlgItems;
  94.         short        x;
  95.         short        y;
  96.         short        cx;
  97.         short        cy;
  98.         WORD        menu;
  99.         WORD        windowClass;
  100.         WCHAR        title[1];
  101.         WORD        pointsize;
  102.         WORD        weight;
  103.         BYTE        italic;
  104.         BYTE        charset;
  105.         WCHAR        typeface[13];
  106.     };
  107.  
  108.     struct DialogTemplateItem {
  109.         DWORD    helpID; 
  110.         DWORD    exStyle; 
  111.         DWORD    style; 
  112.         short    x; 
  113.         short    y; 
  114.         short    cx; 
  115.         short    cy; 
  116.         DWORD    id;                    //DOCERR
  117.     };
  118. #pragma pack(pop)
  119.  
  120.     struct DialogTemplateBuilder {
  121.         std::vector<uint8> data;
  122.  
  123.         DialogTemplateBuilder();
  124.         ~DialogTemplateBuilder();
  125.  
  126.         void push(const void *p, int size) {
  127.             int pos = data.size();
  128.             data.resize(pos + size);
  129.             memcpy(&data[pos], p, size);
  130.         }
  131.  
  132.         void SetRect(int x, int y, int cx, int cy);
  133.         void AddControlBase(DWORD exStyle, DWORD style, int x, int y, int cx, int cy, int id);
  134.         void AddLabel(int id, int x, int y, int cx, int cy, const wchar_t *text);
  135.         void AddCheckbox(int id, int x, int y, int cx, int cy, const wchar_t *text);
  136.         void AddNumericEdit(int id, int x, int y, int cx, int cy);
  137.     };
  138.  
  139.     DialogTemplateBuilder::DialogTemplateBuilder() {
  140.         static const DialogTemplateHeader hdr = {
  141.             1,
  142.             0xFFFF,
  143.             0,
  144.             0,
  145.             WS_TABSTOP | WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | DS_3DLOOK | DS_CONTROL | DS_SHELLFONT,
  146.             0,
  147.             0, 0, 0, 0,
  148.             0,
  149.             0,
  150.             0,
  151.             8,
  152.             FW_NORMAL,
  153.             FALSE,
  154.             ANSI_CHARSET,
  155.             L"MS Shell Dlg"
  156.         };
  157.  
  158.         push(&hdr, sizeof hdr);
  159.     }
  160.  
  161.     DialogTemplateBuilder::~DialogTemplateBuilder() {
  162.     }
  163.  
  164.     void DialogTemplateBuilder::SetRect(int x, int y, int cx, int cy) {
  165.         DialogTemplateHeader& hdr = (DialogTemplateHeader&)data.front();
  166.  
  167.         hdr.x = x;
  168.         hdr.y = y;
  169.         hdr.cx = cx;
  170.         hdr.cy = cy;
  171.     }
  172.  
  173.     void DialogTemplateBuilder::AddControlBase(DWORD exStyle, DWORD style, int x, int y, int cx, int cy, int id) {
  174.         data.resize((data.size()+3)&~3, 0);
  175.  
  176.         const DialogTemplateItem item = {
  177.             0,
  178.             exStyle,
  179.             style,
  180.             x,
  181.             y,
  182.             cx,
  183.             cy,
  184.             id
  185.         };
  186.  
  187.         push(&item, sizeof item);
  188.  
  189.         DialogTemplateHeader& hdr = (DialogTemplateHeader&)data.front();
  190.         ++hdr.cDlgItems;
  191.  
  192.         hdr.cx = std::max<int>(hdr.cx, x+cx);
  193.         hdr.cy = std::max<int>(hdr.cy, y+cy);
  194.     }
  195.  
  196.     void DialogTemplateBuilder::AddLabel(int id, int x, int y, int cx, int cy, const wchar_t *text) {
  197.         AddControlBase(0, WS_CHILD | WS_VISIBLE | SS_LEFT | SS_CENTERIMAGE, x, y, cx, cy, id);
  198.  
  199.         const WORD wclass[2]={0xffff,0x0082};
  200.         push(wclass, sizeof wclass);
  201.  
  202.         push(text, (wcslen(text)+1)*sizeof(WORD));
  203.  
  204.         const WORD extradata = 0;
  205.         push(&extradata, sizeof(WORD));
  206.     }
  207.  
  208.     void DialogTemplateBuilder::AddCheckbox(int id, int x, int y, int cx, int cy, const wchar_t *text) {
  209.         AddControlBase(0, WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | WS_TABSTOP, x, y, cx, cy, id);
  210.  
  211.         const WORD wclass[2]={0xffff,0x0080};
  212.         push(wclass, sizeof wclass);
  213.  
  214.         push(text, (wcslen(text)+1)*sizeof(WORD));
  215.  
  216.         const WORD extradata = 0;
  217.         push(&extradata, sizeof(WORD));
  218.     }
  219.  
  220.     void DialogTemplateBuilder::AddNumericEdit(int id, int x, int y, int cx, int cy) {
  221.         AddControlBase(WS_EX_CLIENTEDGE, WS_CHILD | WS_VISIBLE | ES_NUMBER | WS_TABSTOP, x, y, cx, cy, id);
  222.  
  223.         const WORD wclassandtitle[4]={0xffff,0x0081,0x0030,0x0000};
  224.         push(wclassandtitle, sizeof wclassandtitle);
  225.  
  226.         const WORD extradata = 0;
  227.         push(&extradata, sizeof(WORD));
  228.     }
  229. }
  230.  
  231. #if 0
  232. struct tester {
  233.     static BOOL CALLBACK dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
  234.         return FALSE;
  235.     }
  236.     tester() {
  237.         DialogTemplateBuilder builder;
  238.  
  239.         builder.AddLabel(1, 7, 7, 50, 14, L"hellow");
  240.         builder.AddLabel(1, 7, 21, 50, 14, L"byebye");
  241.  
  242.         DialogBoxIndirect(GetModuleHandle(0), (LPCDLGTEMPLATE)&builder.data.front(), NULL, dlgproc);
  243.     }
  244. } g;
  245. #endif
  246.  
  247. ///////////////////////////////////////////////////////////////////////////
  248.  
  249. void VDInitFilespecSystem() {
  250.     if (!g_pFilespecMap) {
  251.         // This ensures the filespec map will be destroyed before any global destructors.
  252.         static vdautoptr<tFilespecMap> spFilespecMap(new tFilespecMap);
  253.         g_pFilespecMap = spFilespecMap;
  254.     }
  255. }
  256.  
  257. void VDSaveFilespecSystemData() {
  258.     vdsynchronized(g_csFilespecMap) {
  259.         if (g_pFilespecMap) {
  260.             VDRegistryAppKey key("Saved filespecs");
  261.  
  262.             for(tFilespecMap::const_iterator it(g_pFilespecMap->begin()), itEnd(g_pFilespecMap->end()); it!=itEnd; ++it) {
  263.                 long id = it->first;
  264.                 const FilespecEntry& fse = it->second;
  265.                 char buf[16];
  266.  
  267.                 sprintf(buf, "%08x", id);
  268.                 key.setString(buf, VDFileSplitPathLeft(VDStringW(fse.szFile)).c_str());
  269.             }
  270.         }
  271.     }
  272. }
  273.  
  274. void VDLoadFilespecSystemData() {
  275.     vdsynchronized(g_csFilespecMap) {
  276.         VDInitFilespecSystem();
  277.  
  278.         if (g_pFilespecMap) {
  279.             VDRegistryAppKey key("Saved filespecs");
  280.             VDRegistryValueIterator it(key);
  281.  
  282.             VDStringW value;
  283.             while(const char *s = it.Next()) {
  284.                 unsigned long specKey = strtoul(s, NULL, 16);
  285.                 if (key.getString(s, value))
  286.                     VDSetLastLoadSavePath(specKey, value.c_str());
  287.             }
  288.         }
  289.     }
  290. }
  291.  
  292. struct VDGetFileNameHook {
  293.     static UINT_PTR CALLBACK HookFn(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) {
  294.         VDGetFileNameHook *pThis = (VDGetFileNameHook *)GetWindowLongPtr(hdlg, DWLP_USER);
  295.  
  296.         switch(uiMsg) {
  297.         case WM_INITDIALOG:
  298.             pThis = (VDGetFileNameHook *)(((const OPENFILENAMEA *)lParam)->lCustData);
  299.             SetWindowLongPtr(hdlg, DWLP_USER, (LONG_PTR)pThis);
  300.             pThis->Init(hdlg);
  301.             return 0;
  302.  
  303.         case WM_NOTIFY:
  304.             switch(((const NMHDR *)lParam)->code) {
  305.             case CDN_FILEOK:
  306.                 return !pThis->Writeback(hdlg);
  307.             }
  308.         }
  309.  
  310.         return 0;
  311.     }
  312.  
  313.     void Init(HWND hdlg) {
  314.         for(int nOpts = 0; mpOpts[nOpts].mType; ++nOpts) {
  315.             const VDFileDialogOption& opt = mpOpts[nOpts];
  316.             const int id = 1000 + 16*nOpts;
  317.  
  318.             switch(opt.mType) {
  319.             case VDFileDialogOption::kBool:
  320.                 CheckDlgButton(hdlg, id, !!mpOptVals[opt.mDstIdx]);
  321.                 break;
  322.             case VDFileDialogOption::kInt:
  323.                 SetDlgItemInt(hdlg, id, mpOptVals[opt.mDstIdx], TRUE);
  324.                 break;
  325.             case VDFileDialogOption::kEnabledInt:
  326.                 CheckDlgButton(hdlg, id, !!mpOptVals[opt.mDstIdx]);
  327.                 SetDlgItemInt(hdlg, id+1, mpOptVals[opt.mDstIdx+1], TRUE);
  328.                 break;
  329.             }
  330.         }
  331.     }
  332.  
  333.     bool Writeback(HWND hdlg) {
  334.         for(int nOpts = 0; mpOpts[nOpts].mType; ++nOpts) {
  335.             const VDFileDialogOption& opt = mpOpts[nOpts];
  336.             const int id = 1000 + 16*nOpts;
  337.             BOOL bOk;
  338.             INT v;
  339.  
  340.             switch(opt.mType) {
  341.             case VDFileDialogOption::kBool:
  342.                 mpOptVals[opt.mDstIdx] = !!IsDlgButtonChecked(hdlg, id);
  343.                 break;
  344.             case VDFileDialogOption::kInt:
  345.                 v = GetDlgItemInt(hdlg, id, &bOk, TRUE);
  346.                 if (!bOk) {
  347.                     MessageBeep(MB_ICONEXCLAMATION);
  348.                     SetFocus(GetDlgItem(hdlg, id));
  349.                     return false;
  350.                 }
  351.                 mpOptVals[opt.mDstIdx] = v;
  352.                 break;
  353.             case VDFileDialogOption::kEnabledInt:
  354.                 mpOptVals[opt.mDstIdx] = !!IsDlgButtonChecked(hdlg, id);
  355.                 if (mpOptVals[opt.mDstIdx]) {
  356.                     v = GetDlgItemInt(hdlg, id+1, &bOk, TRUE);
  357.                     if (!bOk) {
  358.                         MessageBeep(MB_ICONEXCLAMATION);
  359.                         SetFocus(GetDlgItem(hdlg, id+1));
  360.                         return false;
  361.                     }
  362.                     mpOptVals[opt.mDstIdx+1] = v;
  363.                 }
  364.                 break;
  365.             }
  366.         }
  367.         return true;
  368.     }
  369.  
  370.     const VDFileDialogOption *mpOpts;
  371.     int *mpOptVals;
  372. };
  373.  
  374. static const VDStringW VDGetFileName(bool bSaveAs, long nKey, VDGUIHandle ctxParent, const wchar_t *pszTitle, const wchar_t *pszFilters, const wchar_t *pszExt, const VDFileDialogOption *pOptions, int *pOptVals) {
  375.     FilespecEntry fsent;
  376.     tFilespecMap::iterator it;
  377.  
  378.     vdsynchronized(g_csFilespecMap) {
  379.         VDInitFilespecSystem();
  380.         
  381.         it = g_pFilespecMap->find(nKey);
  382.  
  383.         if (it == g_pFilespecMap->end()) {
  384.             std::pair<tFilespecMap::iterator, bool> r = g_pFilespecMap->insert(tFilespecMap::value_type(nKey, FilespecEntry()));
  385.  
  386.             if (!r.second) {
  387.                 VDStringW empty;
  388.                 return empty;
  389.             }
  390.  
  391.             it = r.first;
  392.  
  393.             (*it).second.szFile[0] = 0;
  394.         }
  395.  
  396.         fsent = (*it).second;
  397.     }
  398.  
  399.     VDASSERTCT(sizeof(OPENFILENAMEA) == sizeof(OPENFILENAMEW));
  400.     union {
  401.         OPENFILENAMEA a;
  402.         OPENFILENAMEW w;
  403.     } ofn={0};
  404.  
  405.     // Slight annoyance: If we want to use custom templates and still keep the places
  406.     // bar, the lStructSize parameter must be greater than OPENFILENAME_SIZE_VERSION_400.
  407.     // But if sizeof(OPENFILENAME) is used under Windows 95/98, the open call fails.
  408.     // Argh.
  409.  
  410.     bool bIsAtLeastWin2K = (sint32)(GetVersion() & 0x800000FF) >= 5;    // OS must be NT, major version >= 5
  411.  
  412.     ofn.w.lStructSize        = bIsAtLeastWin2K ? sizeof(OPENFILENAME) : OPENFILENAME_SIZE_VERSION_400;
  413.     ofn.w.hwndOwner            = (HWND)ctxParent;
  414.     ofn.w.lpstrCustomFilter    = NULL;
  415.     ofn.w.nFilterIndex        = 0;
  416.     ofn.w.lpstrFileTitle    = NULL;
  417.     ofn.w.lpstrInitialDir    = NULL;
  418.     ofn.w.Flags                = OFN_PATHMUSTEXIST|OFN_ENABLESIZING|OFN_EXPLORER|OFN_OVERWRITEPROMPT|OFN_HIDEREADONLY;
  419.  
  420.     if (bSaveAs)
  421.         ofn.w.Flags |= OFN_OVERWRITEPROMPT;
  422.     else
  423.         ofn.w.Flags |= OFN_FILEMUSTEXIST;
  424.  
  425.     DialogTemplateBuilder builder;
  426.     VDGetFileNameHook hook = { pOptions, pOptVals };
  427.     int nReadOnlyIndex = -1;
  428.     int nSelectedFilterIndex = -1;
  429.  
  430.     if (pOptions) {
  431.         int y = 0;
  432.  
  433.         for(int nOpts = 0; pOptions[nOpts].mType; ++nOpts) {
  434.             const VDFileDialogOption& opt = pOptions[nOpts];
  435.             const wchar_t *s = opt.mpLabel;
  436.             const int id = 1000 + 16*nOpts;
  437.             const int sw = s ? 4*wcslen(s) : 0;
  438.  
  439.             switch(pOptions[nOpts].mType) {
  440.             case VDFileDialogOption::kBool:
  441.                 builder.AddCheckbox(id, 5, y, 10+sw, 12, s);
  442.                 y += 12;
  443.                 break;
  444.             case VDFileDialogOption::kInt:
  445.                 builder.AddLabel(0, 5, y, sw, 12, s);
  446.                 builder.AddNumericEdit(id, 9+sw, y, 50, 12);
  447.                 y += 12;
  448.                 break;
  449.             case VDFileDialogOption::kEnabledInt:
  450.                 builder.AddCheckbox(id, 5, y+1, 10+sw, 12, s);
  451.                 builder.AddNumericEdit(id+1, 19+sw, y+1, 50, 12);
  452.                 y += 14;
  453.                 break;
  454.             case VDFileDialogOption::kReadOnly:
  455.                 VDASSERT(nReadOnlyIndex < 0);
  456.                 nReadOnlyIndex = opt.mDstIdx;
  457.                 ofn.w.Flags &= ~OFN_HIDEREADONLY;
  458.                 if (pOptVals[nReadOnlyIndex])
  459.                     ofn.w.Flags |= OFN_READONLY;
  460.                 break;
  461.             case VDFileDialogOption::kSelectedFilter:
  462.                 VDASSERT(nSelectedFilterIndex < 0);
  463.                 nSelectedFilterIndex = opt.mDstIdx;
  464.                 ofn.w.nFilterIndex = pOptVals[opt.mDstIdx];
  465.                 break;
  466.             case VDFileDialogOption::kConfirmFile:
  467.                 if (!pOptVals[opt.mDstIdx])
  468.                     ofn.w.Flags &= ~OFN_OVERWRITEPROMPT;
  469.                 break;
  470.             }
  471.         }
  472.  
  473.         if (y > 0) {
  474.             ofn.w.Flags        |= OFN_ENABLETEMPLATEHANDLE | OFN_ENABLEHOOK;
  475.             ofn.w.hInstance = (HINSTANCE)&builder.data.front();
  476.             ofn.w.lpfnHook    = VDGetFileNameHook::HookFn;
  477.             ofn.w.lCustData    = (LPARAM)&hook;
  478.         }
  479.     }
  480.  
  481.     bool existingFileName = false;
  482.     if (fsent.szFile[0]) {
  483.         wchar_t lastChar = fsent.szFile[wcslen(fsent.szFile) - 1];
  484.  
  485.         if (lastChar != '\\' && lastChar != ':')
  486.             existingFileName = true;
  487.     }
  488.  
  489.     VDStringW strFilename;
  490.     bool bSuccess = false;
  491.  
  492.     // A little gotcha here:
  493.     //
  494.     // Docs for OPENFILENAME say that lpstrInitialFile is used, otherwise a path in
  495.     // lpstrFile is used (2000/XP). What it doesn't mention is that if lpstrFile
  496.     // points to only a directory, not only won't it use that path, but it will also
  497.     // ignore any path in lpstrInitialDir. So we have to make sure that we either
  498.     // supply a full file path in lpstrFile or a directory path in lpstrInitialDir,
  499.     // but never the same in both.
  500.  
  501.     if ((sint32)GetVersion() < 0) {        // Windows 95/98
  502.         VDStringA strFilters(VDTextWToA(pszFilters, FileFilterLength(pszFilters)));
  503.         VDStringA strDefExt(VDTextWToA(pszExt, -1));
  504.         VDStringA strTitle(VDTextWToA(pszTitle, -1));
  505.  
  506.         char szInitialPath[MAX_PATH];
  507.         char szFile[MAX_PATH];
  508.  
  509.         if (existingFileName) {
  510.             VDTextWToA(szFile, sizeof szFile, fsent.szFile, -1);
  511.         } else {
  512.             szFile[0] = 0;
  513.             VDTextWToA(szInitialPath, sizeof szInitialPath, fsent.szFile, -1);
  514.         }
  515.  
  516.         ofn.a.lpstrFilter        = strFilters.c_str();
  517.         ofn.a.lpstrFile            = szFile;
  518.         ofn.a.nMaxFile            = sizeof(szFile) / sizeof(szFile[0]);
  519.         ofn.a.lpstrTitle        = strTitle.c_str();
  520.         ofn.a.lpstrDefExt        = strDefExt.c_str();
  521.         ofn.a.lpstrInitialDir    = existingFileName ? NULL : szInitialPath;
  522.  
  523.         BOOL (WINAPI *pfn)(OPENFILENAMEA *) = (bSaveAs ? GetSaveFileNameA : GetOpenFileNameA);
  524.         BOOL result = pfn(&ofn.a);
  525.  
  526.         // If the last path is no longer valid the dialog may fail to initialize, so if it's not
  527.         // a cancel we retry with no preset filename.
  528.         if (!result && CommDlgExtendedError()) {
  529.             szFile[0] = 0;
  530.             result = pfn(&ofn.a);
  531.         }
  532.  
  533.         if (result) {
  534.             VDTextAToW(fsent.szFile, sizeof(fsent.szFile)/sizeof(fsent.szFile[0]), szFile, -1);
  535.             bSuccess = true;
  536.         }
  537.     } else {
  538.         wchar_t wszFile[MAX_PATH];
  539.  
  540.         if (existingFileName)
  541.             wcsncpyz(wszFile, fsent.szFile, MAX_PATH);
  542.         else
  543.             wszFile[0] = 0;
  544.  
  545.         ofn.w.lpstrFilter        = pszFilters;
  546.         ofn.w.lpstrFile            = wszFile;
  547.         ofn.w.nMaxFile            = MAX_PATH;
  548.         ofn.w.lpstrTitle        = pszTitle;
  549.         ofn.w.lpstrDefExt        = pszExt;
  550.         ofn.w.lpstrInitialDir    = existingFileName ? NULL : fsent.szFile;
  551.  
  552.         BOOL (WINAPI *pfn)(OPENFILENAMEW *) = (bSaveAs ? GetSaveFileNameW : GetOpenFileNameW);
  553.         BOOL result = pfn(&ofn.w);
  554.  
  555.         // If the last path is no longer valid the dialog may fail to initialize, so if it's not
  556.         // a cancel we retry with no preset filename.
  557.         if (!result && CommDlgExtendedError()) {
  558.             wszFile[0] = 0;
  559.             ofn.w.lpstrInitialDir = NULL;
  560.             result = pfn(&ofn.w);
  561.         }
  562.  
  563.         if (result) {
  564.             wcsncpyz(fsent.szFile, wszFile, MAX_PATH);
  565.             bSuccess = true;
  566.         }
  567.     }
  568.  
  569.     if (bSuccess) {
  570.         if (nReadOnlyIndex >= 0)
  571.             pOptVals[nReadOnlyIndex] = !!(ofn.w.Flags & OFN_READONLY);
  572.  
  573.         if (nSelectedFilterIndex >= 0)
  574.             pOptVals[nSelectedFilterIndex] = ofn.w.nFilterIndex;
  575.  
  576.         strFilename = fsent.szFile;
  577.     }
  578.  
  579.     if (bSuccess) {
  580.         vdsynchronized(g_csFilespecMap) {
  581.             (*it).second = fsent;
  582.         }
  583.     }
  584.  
  585.     return strFilename;
  586. }
  587.  
  588. const VDStringW VDGetLoadFileName(long nKey, VDGUIHandle ctxParent, const wchar_t *pszTitle, const wchar_t *pszFilters, const wchar_t *pszExt, const VDFileDialogOption *pOptions, int *pOptVals) {
  589.     return VDGetFileName(false, nKey, ctxParent, pszTitle, pszFilters, pszExt, pOptions, pOptVals);
  590. }
  591.  
  592. const VDStringW VDGetSaveFileName(long nKey, VDGUIHandle ctxParent, const wchar_t *pszTitle, const wchar_t *pszFilters, const wchar_t *pszExt, const VDFileDialogOption *pOptions, int *pOptVals) {
  593.     return VDGetFileName(true, nKey, ctxParent, pszTitle, pszFilters, pszExt, pOptions, pOptVals);
  594. }
  595.  
  596. void VDSetLastLoadSavePath(long nKey, const wchar_t *path) {
  597.     vdsynchronized(g_csFilespecMap) {
  598.         VDInitFilespecSystem();
  599.         
  600.         tFilespecMap::iterator it = g_pFilespecMap->find(nKey);
  601.  
  602.         if (it == g_pFilespecMap->end()) {
  603.             std::pair<tFilespecMap::iterator, bool> r = g_pFilespecMap->insert(tFilespecMap::value_type(nKey, FilespecEntry()));
  604.  
  605.             if (!r.second)
  606.                 return;
  607.  
  608.             it = r.first;
  609.         }
  610.  
  611.         FilespecEntry& fsent = (*it).second;
  612.  
  613.         wcsncpyz(fsent.szFile, path, sizeof fsent.szFile / sizeof fsent.szFile[0]);
  614.     }
  615. }
  616.  
  617. const VDStringW VDGetLastLoadSavePath(long nKey) {
  618.     VDStringW result;
  619.  
  620.     vdsynchronized(g_csFilespecMap) {
  621.         VDInitFilespecSystem();
  622.         
  623.         tFilespecMap::iterator it = g_pFilespecMap->find(nKey);
  624.  
  625.         if (it != g_pFilespecMap->end()) {
  626.             FilespecEntry& fsent = (*it).second;
  627.  
  628.             result = fsent.szFile;
  629.         }
  630.     }
  631.  
  632.     return result;
  633. }
  634.  
  635. void VDSetLastLoadSaveFileName(long nKey, const wchar_t *fileName) {
  636.     vdsynchronized(g_csFilespecMap) {
  637.         VDInitFilespecSystem();
  638.         
  639.         tFilespecMap::iterator it = g_pFilespecMap->find(nKey);
  640.  
  641.         if (it == g_pFilespecMap->end()) {
  642.             std::pair<tFilespecMap::iterator, bool> r = g_pFilespecMap->insert(tFilespecMap::value_type(nKey, FilespecEntry()));
  643.  
  644.             if (!r.second)
  645.                 return;
  646.  
  647.             it = r.first;
  648.         }
  649.  
  650.         FilespecEntry& fsent = (*it).second;
  651.  
  652.         VDStringW newPath(VDMakePath(VDFileSplitPathLeft(VDStringW(fsent.szFile)).c_str(), fileName));
  653.  
  654.         wcsncpyz(fsent.szFile, newPath.c_str(), sizeof fsent.szFile / sizeof fsent.szFile[0]);
  655.     }
  656. }
  657.  
  658. ///////////////////////////////////////////////////////////////////////////
  659.  
  660. struct DirspecEntry {
  661.     wchar_t szFile[MAX_PATH];
  662. };
  663.  
  664. typedef std::map<long, DirspecEntry> tDirspecMap;
  665. tDirspecMap *g_pDirspecMap;
  666.  
  667. ///////////////////////////////////////////////////////////////////////////
  668.  
  669. const VDStringW VDGetDirectory(long nKey, VDGUIHandle ctxParent, const wchar_t *pszTitle) {
  670.     if (!g_pDirspecMap)
  671.         g_pDirspecMap = new tDirspecMap;
  672.  
  673.     tDirspecMap::iterator it = g_pDirspecMap->find(nKey);
  674.  
  675.     if (it == g_pDirspecMap->end()) {
  676.         std::pair<tDirspecMap::iterator, bool> r = g_pDirspecMap->insert(tDirspecMap::value_type(nKey, DirspecEntry()));
  677.  
  678.         if (!r.second) {
  679.             VDStringW empty;
  680.             return empty;
  681.         }
  682.  
  683.         it = r.first;
  684.  
  685.         (*it).second.szFile[0] = 0;
  686.     }
  687.  
  688.     DirspecEntry& fsent = (*it).second;
  689.     bool bSuccess = false;
  690.  
  691.     if (SUCCEEDED(CoInitialize(NULL))) {
  692.         IMalloc *pMalloc;
  693.  
  694.         if (SUCCEEDED(SHGetMalloc(&pMalloc))) {
  695.  
  696.             if ((LONG)GetVersion() < 0) {        // Windows 9x
  697.                 char *pszBuffer;
  698.  
  699.                 if (pszBuffer = (char *)pMalloc->Alloc(MAX_PATH)) {
  700.                     BROWSEINFOA bi;
  701.                     ITEMIDLIST *pidlBrowse;
  702.  
  703.                     VDStringA tempA(VDTextWToA(pszTitle));
  704.  
  705.                     bi.hwndOwner        = (HWND)ctxParent;
  706.                     bi.pidlRoot            = NULL;
  707.                     bi.pszDisplayName    = pszBuffer;
  708.                     bi.lpszTitle        = tempA.c_str();
  709.                     bi.ulFlags            = BIF_EDITBOX | /*BIF_NEWDIALOGSTYLE |*/ BIF_RETURNONLYFSDIRS | BIF_VALIDATE;
  710.                     bi.lpfn                = NULL;
  711.  
  712.                     if (pidlBrowse = SHBrowseForFolderA(&bi)) {
  713.                         if (SHGetPathFromIDListA(pidlBrowse, pszBuffer)) {
  714.                             VDTextAToW(fsent.szFile, MAX_PATH, pszBuffer);
  715.                             bSuccess = true;
  716.                         }
  717.  
  718.                         pMalloc->Free(pidlBrowse);
  719.                     }
  720.                     pMalloc->Free(pszBuffer);
  721.                 }
  722.             } else {
  723.                 HMODULE hmod = GetModuleHandle("shell32.dll");        // We know shell32 is loaded because we hard link to SHBrowseForFolderA.
  724.                 typedef LPITEMIDLIST (APIENTRY *tpSHBrowseForFolderW)(LPBROWSEINFOW);
  725.                 typedef BOOL (APIENTRY *tpSHGetPathFromIDListW)(LPCITEMIDLIST pidl, LPWSTR pszPath);
  726.                 tpSHBrowseForFolderW pSHBrowseForFolderW = (tpSHBrowseForFolderW)GetProcAddress(hmod, "SHBrowseForFolderW");
  727.                 tpSHGetPathFromIDListW pSHGetPathFromIDListW = (tpSHGetPathFromIDListW)GetProcAddress(hmod, "SHGetPathFromIDListW");
  728.  
  729.                 if (pSHBrowseForFolderW && pSHGetPathFromIDListW) {
  730.                     if (wchar_t *pszBuffer = (wchar_t *)pMalloc->Alloc(MAX_PATH * sizeof(wchar_t))) {
  731.                         BROWSEINFOW bi;
  732.                         ITEMIDLIST *pidlBrowse;
  733.  
  734.                         bi.hwndOwner        = (HWND)ctxParent;
  735.                         bi.pidlRoot            = NULL;
  736.                         bi.pszDisplayName    = pszBuffer;
  737.                         bi.lpszTitle        = pszTitle;
  738.                         bi.ulFlags            = BIF_EDITBOX | /*BIF_NEWDIALOGSTYLE |*/ BIF_RETURNONLYFSDIRS | BIF_VALIDATE;
  739.                         bi.lpfn                = NULL;
  740.  
  741.                         if (pidlBrowse = pSHBrowseForFolderW(&bi)) {
  742.                             if (pSHGetPathFromIDListW(pidlBrowse, pszBuffer)) {
  743.                                 wcscpy(fsent.szFile, pszBuffer);
  744.                                 bSuccess = true;
  745.                             }
  746.  
  747.                             pMalloc->Free(pidlBrowse);
  748.                         }
  749.                         pMalloc->Free(pszBuffer);
  750.                     }
  751.                 }
  752.             }
  753.         }
  754.  
  755.         CoUninitialize();
  756.     }
  757.  
  758.     VDStringW strDir;
  759.  
  760.     if (bSuccess)
  761.         strDir = fsent.szFile;
  762.  
  763.     return strDir;
  764. }
  765.