1// Windows/CommonDialog.cpp
2
3#include "StdAfx.h"
4
5#include "../Common/MyBuffer.h"
6
7#ifdef UNDER_CE
8#include <commdlg.h>
9#endif
10
11#ifndef _UNICODE
12#include "../Common/StringConvert.h"
13#endif
14
15#include "CommonDialog.h"
16#include "Defs.h"
17// #include "FileDir.h"
18
19#ifndef _UNICODE
20extern bool g_IsNT;
21#endif
22
23namespace NWindows {
24
25/*
26  GetSaveFileName()
27  GetOpenFileName()
28  OPENFILENAME
29
30(lpstrInitialDir) : the initial directory.
31DOCs: the algorithm for selecting the initial directory varies on different platforms:
32{
33  Win2000/XP/Vista:
34    1. If lpstrFile contains a path, that path is the initial directory.
35    2. Otherwise, lpstrInitialDir specifies the initial directory.
36
37  Win7:
38    If lpstrInitialDir has the same value as was passed the first time
39    the application used an Open or Save As dialog box, the path
40    most recently selected by the user is used as the initial directory.
41}
42
43Win10:
44 in:
45  function supports (lpstrInitialDir) path with super prefix "\\\\?\\"
46  function supports (lpstrInitialDir) path with long path
47  function doesn't support absolute (lpstrFile) path with super prefix "\\\\?\\"
48  function doesn't support absolute (lpstrFile) path with long path
49 out: the path with super prefix "\\\\?\\" will be returned, if selected path is long
50
51WinXP-64 and Win10: if no filters, the system shows all files.
52    but DOCs say: If all three members are zero or NULL,
53        the system does not use any filters and does not
54        show any files in the file list control of the dialog box.
55
56in Win7+: GetOpenFileName() and GetSaveFileName()
57    do not support pstrCustomFilter feature anymore
58*/
59
60#ifdef UNDER_CE
61#define MY_OFN_PROJECT  0x00400000
62#define MY_OFN_SHOW_ALL 0x01000000
63#endif
64
65
66/*
67structures
68  OPENFILENAMEW
69  OPENFILENAMEA
70contain additional members:
71#if (_WIN32_WINNT >= 0x0500)
72  void *pvReserved;
73  DWORD dwReserved;
74  DWORD FlagsEx;
75#endif
76
77If we compile the source code with (_WIN32_WINNT >= 0x0500), some functions
78will not work at NT 4.0, if we use sizeof(OPENFILENAME).
79We try to use reduced structure OPENFILENAME_NT4.
80*/
81
82// #if defined(_WIN64) || (defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500)
83#if defined(__GNUC__) && (__GNUC__ <= 9) || defined(Z7_OLD_WIN_SDK)
84  #ifndef _UNICODE
85  #define my_compatib_OPENFILENAMEA       OPENFILENAMEA
86  #endif
87  #define my_compatib_OPENFILENAMEW       OPENFILENAMEW
88
89  // MinGW doesn't support some required macros. So we define them here:
90  #ifndef CDSIZEOF_STRUCT
91  #define CDSIZEOF_STRUCT(structname, member)  (((int)((LPBYTE)(&((structname*)0)->member) - ((LPBYTE)((structname*)0)))) + sizeof(((structname*)0)->member))
92  #endif
93  #ifndef _UNICODE
94  #ifndef OPENFILENAME_SIZE_VERSION_400A
95  #define OPENFILENAME_SIZE_VERSION_400A  CDSIZEOF_STRUCT(OPENFILENAMEA,lpTemplateName)
96  #endif
97  #endif
98  #ifndef OPENFILENAME_SIZE_VERSION_400W
99  #define OPENFILENAME_SIZE_VERSION_400W  CDSIZEOF_STRUCT(OPENFILENAMEW,lpTemplateName)
100  #endif
101
102  #ifndef _UNICODE
103  #define my_compatib_OPENFILENAMEA_size OPENFILENAME_SIZE_VERSION_400A
104  #endif
105  #define my_compatib_OPENFILENAMEW_size OPENFILENAME_SIZE_VERSION_400W
106#else
107  #ifndef _UNICODE
108  #define my_compatib_OPENFILENAMEA       OPENFILENAME_NT4A
109  #define my_compatib_OPENFILENAMEA_size  sizeof(my_compatib_OPENFILENAMEA)
110  #endif
111  #define my_compatib_OPENFILENAMEW       OPENFILENAME_NT4W
112  #define my_compatib_OPENFILENAMEW_size  sizeof(my_compatib_OPENFILENAMEW)
113#endif
114/*
115#elif defined(UNDER_CE) || defined(_WIN64) || (_WIN32_WINNT < 0x0500)
116// || !defined(WINVER)
117  #ifndef _UNICODE
118  #define my_compatib_OPENFILENAMEA       OPENFILENAMEA
119  #define my_compatib_OPENFILENAMEA_size sizeof(OPENFILENAMEA)
120  #endif
121  #define my_compatib_OPENFILENAMEW       OPENFILENAMEW
122  #define my_compatib_OPENFILENAMEW_size sizeof(OPENFILENAMEW)
123#else
124
125#endif
126*/
127
128#ifndef _UNICODE
129#define CONV_U_To_A(dest, src, temp) AString temp; if (src) { temp = GetSystemString(src); dest = temp; }
130#endif
131
132bool CCommonDialogInfo::CommonDlg_BrowseForFile(LPCWSTR lpstrInitialDir, const UStringVector &filters)
133{
134  /* GetSaveFileName() and GetOpenFileName() could change current dir,
135     if OFN_NOCHANGEDIR is not used.
136     We can restore current dir manually, if it's required.
137     22.02: we use OFN_NOCHANGEDIR. So we don't need to restore current dir manually. */
138  // NFile::NDir::CCurrentDirRestorer curDirRestorer;
139
140#ifndef _UNICODE
141  if (!g_IsNT)
142  {
143    AString tempPath;
144    AStringVector f;
145    unsigned i;
146    for (i = 0; i < filters.Size(); i++)
147      f.Add(GetSystemString(filters[i]));
148    unsigned size = f.Size() + 1;
149    for (i = 0; i < f.Size(); i++)
150      size += f[i].Len();
151    CObjArray<char> filterBuf(size);
152    // memset(filterBuf, 0, size * sizeof(char));
153    {
154      char *dest = filterBuf;
155      for (i = 0; i < f.Size(); i++)
156      {
157        const AString &s = f[i];
158        MyStringCopy(dest, s);
159        dest += s.Len() + 1;
160      }
161      *dest = 0;
162    }
163    my_compatib_OPENFILENAMEA p;
164    memset(&p, 0, sizeof(p));
165    p.lStructSize = my_compatib_OPENFILENAMEA_size;
166    p.hwndOwner = hwndOwner;
167    if (size > 1)
168    {
169      p.lpstrFilter = filterBuf;
170      p.nFilterIndex = (DWORD)(FilterIndex + 1);
171    }
172
173    CONV_U_To_A(p.lpstrInitialDir, lpstrInitialDir, initialDir_a)
174    CONV_U_To_A(p.lpstrTitle, lpstrTitle, title_a)
175
176    const AString filePath_a = GetSystemString(FilePath);
177    const unsigned bufSize = MAX_PATH * 8
178        + filePath_a.Len()
179        + initialDir_a.Len();
180    p.nMaxFile = bufSize;
181    p.lpstrFile = tempPath.GetBuf(bufSize);
182    MyStringCopy(p.lpstrFile, filePath_a);
183    p.Flags =
184          OFN_EXPLORER
185        | OFN_HIDEREADONLY
186        | OFN_NOCHANGEDIR;
187    const BOOL b = SaveMode ?
188        ::GetSaveFileNameA((LPOPENFILENAMEA)(void *)&p) :
189        ::GetOpenFileNameA((LPOPENFILENAMEA)(void *)&p);
190    if (!b)
191      return false;
192    {
193      tempPath.ReleaseBuf_CalcLen(bufSize);
194      FilePath = GetUnicodeString(tempPath);
195      FilterIndex = (int)p.nFilterIndex - 1;
196      return true;
197    }
198  }
199  else
200#endif
201  {
202    UString tempPath;
203    unsigned size = filters.Size() + 1;
204    unsigned i;
205    for (i = 0; i < filters.Size(); i++)
206      size += filters[i].Len();
207    CObjArray<wchar_t> filterBuf(size);
208    // memset(filterBuf, 0, size * sizeof(wchar_t));
209    {
210      wchar_t *dest = filterBuf;
211      for (i = 0; i < filters.Size(); i++)
212      {
213        const UString &s = filters[i];
214        MyStringCopy(dest, s);
215        dest += s.Len() + 1;
216      }
217      *dest = 0;
218      // if ((unsigned)(dest + 1 - filterBuf) != size) return false;
219    }
220    my_compatib_OPENFILENAMEW p;
221    memset(&p, 0, sizeof(p));
222    p.lStructSize = my_compatib_OPENFILENAMEW_size;
223    p.hwndOwner = hwndOwner;
224    if (size > 1)
225    {
226      p.lpstrFilter = filterBuf;
227      p.nFilterIndex = (DWORD)(FilterIndex + 1);
228    }
229    unsigned bufSize = MAX_PATH * 8 + FilePath.Len();
230    if (lpstrInitialDir)
231    {
232      p.lpstrInitialDir = lpstrInitialDir;
233      bufSize += MyStringLen(lpstrInitialDir);
234    }
235    p.nMaxFile = bufSize;
236    p.lpstrFile = tempPath.GetBuf(bufSize);
237    MyStringCopy(p.lpstrFile, FilePath);
238    p.lpstrTitle = lpstrTitle;
239    p.Flags =
240          OFN_EXPLORER
241        | OFN_HIDEREADONLY
242        | OFN_NOCHANGEDIR
243        // | OFN_FORCESHOWHIDDEN // Win10 shows hidden items even without this flag
244        // | OFN_PATHMUSTEXIST
245      #ifdef UNDER_CE
246        | (OpenFolderMode ? (MY_OFN_PROJECT | MY_OFN_SHOW_ALL) : 0)
247      #endif
248        ;
249    const BOOL b = SaveMode ?
250        ::GetSaveFileNameW((LPOPENFILENAMEW)(void *)&p) :
251        ::GetOpenFileNameW((LPOPENFILENAMEW)(void *)&p);
252    /* DOCs: lpstrFile :
253        if the buffer is too small, then:
254        - the function returns FALSE
255        - the CommDlgExtendedError() returns FNERR_BUFFERTOOSMALL
256        - the first two bytes of the lpstrFile buffer contain the
257          required size, in bytes or characters. */
258    if (!b)
259      return false;
260    {
261      tempPath.ReleaseBuf_CalcLen(bufSize);
262      FilePath = tempPath;
263      FilterIndex = (int)p.nFilterIndex - 1;
264      return true;
265    }
266  }
267}
268
269}
270