xref: /trunk/main/fpicker/source/win32/filepicker/FileOpenDlg.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_fpicker.hxx"
30 
31 //------------------------------------------------------------------------
32 // includes
33 //------------------------------------------------------------------------
34 
35 #include <tchar.h>
36 #include <osl/diagnose.h>
37 #include "../misc/WinImplHelper.hxx"
38 #include "FileOpenDlg.hxx"
39 
40 //------------------------------------------------------------------------
41 // constants
42 //------------------------------------------------------------------------
43 
44 namespace /* private */
45 {
46     // we choose such large buffers because the size of
47     // an single line edit field can be up to 32k; if
48     // a user has a multi selection FilePicker and selects
49     // a lot of files in a large directory we may reach this
50     // limit and don't want to get out of memory;
51     // another much more elegant way would be to subclass the
52     // FileOpen dialog and overload the BM_CLICK event of the
53     // OK button so that we determine the size of the text
54     // currently in the edit field and resize our buffer
55     // appropriately - in the future we will do this
56     const size_t MAX_FILENAME_BUFF_SIZE  = 32000;
57     const size_t MAX_FILETITLE_BUFF_SIZE = 32000;
58     const size_t MAX_FILTER_BUFF_SIZE    = 4096;
59 
60     const LPTSTR CURRENT_INSTANCE = TEXT("CurrInst");
61 
62     //------------------------------------------
63     // find an appropriate parent window
64     //------------------------------------------
65 
66     inline bool is_current_process_window(HWND hwnd)
67     {
68         DWORD pid;
69         GetWindowThreadProcessId(hwnd, &pid);
70         return (pid == GetCurrentProcessId());
71     }
72 
73     HWND choose_parent_window()
74     {
75         HWND hwnd_parent = GetForegroundWindow();
76         if (!is_current_process_window(hwnd_parent))
77             hwnd_parent = GetDesktopWindow();
78 
79         return hwnd_parent;
80     }
81 };
82 
83 //------------------------------------------------------------------------
84 //
85 //------------------------------------------------------------------------
86 
87 CFileOpenDialog::CFileOpenDialog(
88     bool bFileOpenDialog,
89     sal_uInt32 dwFlags,
90     sal_uInt32 dwTemplateId,
91     HINSTANCE hInstance) :
92     m_hwndFileOpenDlg(0),
93     m_hwndFileOpenDlgChild(0),
94     m_bFileOpenDialog(bFileOpenDialog),
95     m_filterBuffer(MAX_FILTER_BUFF_SIZE),
96     m_fileTitleBuffer(MAX_FILETITLE_BUFF_SIZE),
97     m_helperBuffer(MAX_FILENAME_BUFF_SIZE),
98     m_fileNameBuffer(MAX_FILENAME_BUFF_SIZE),
99     m_pfnBaseDlgProc(0)
100 {
101     // initialize the OPENFILENAME struct
102     if (IsWindows2000Platform() || IsWindowsME())
103     {
104         ZeroMemory(&m_ofn, sizeof(m_ofn));
105         m_ofn.lStructSize = sizeof(m_ofn);
106     }
107     else // OSVER < Win2000
108     {
109         // the size of the OPENFILENAME structure is different
110         // under windows < win2000
111         ZeroMemory(&m_ofn, _OPENFILENAME_SIZE_VERSION_400);
112         m_ofn.lStructSize = _OPENFILENAME_SIZE_VERSION_400;
113     }
114 
115     // 0x02000000 for #97681, sfx will make the entry into
116     // the recent document list
117     m_ofn.Flags |= dwFlags |
118                    OFN_EXPLORER |
119                    OFN_ENABLEHOOK |
120                    OFN_HIDEREADONLY |
121                    OFN_PATHMUSTEXIST |
122                    OFN_FILEMUSTEXIST |
123                    OFN_OVERWRITEPROMPT |
124                    OFN_ENABLESIZING |
125                    OFN_DONTADDTORECENT; // 0x02000000 -> OFN_DONTADDTORECENT only available with new platform sdk
126 
127     // it is a little hack but how else could
128     // we get a parent window (using a vcl window?)
129     m_ofn.hwndOwner = choose_parent_window();
130 
131     m_ofn.lpstrFile = reinterpret_cast<LPTSTR>(const_cast<sal_Unicode*>(m_fileNameBuffer.getStr()));
132     m_ofn.nMaxFile  = m_fileNameBuffer.getCapacity();
133 
134     m_ofn.lpstrFileTitle = reinterpret_cast<LPTSTR>(const_cast<sal_Unicode*>(m_fileTitleBuffer.getStr()));
135     m_ofn.nMaxFileTitle  = m_fileTitleBuffer.getCapacity();
136 
137     m_ofn.lpfnHook = CFileOpenDialog::ofnHookProc;
138 
139     // set a custom template
140 
141     if (dwTemplateId)
142     {
143         OSL_ASSERT(hInstance);
144 
145         m_ofn.Flags          |= OFN_ENABLETEMPLATE;
146         m_ofn.lpTemplateName  = MAKEINTRESOURCE(dwTemplateId);
147         m_ofn.hInstance       = hInstance;
148     }
149 
150     // set a pointer to myself as ofn parameter
151     m_ofn.lCustData = reinterpret_cast<long>(this);
152 }
153 
154 //------------------------------------------------------------------------
155 //
156 //------------------------------------------------------------------------
157 
158 CFileOpenDialog::~CFileOpenDialog()
159 {
160 }
161 
162 //------------------------------------------------------------------------
163 //
164 //------------------------------------------------------------------------
165 
166 void SAL_CALL CFileOpenDialog::setTitle(const rtl::OUString& aTitle)
167 {
168     m_dialogTitle = aTitle;
169     m_ofn.lpstrTitle = reinterpret_cast<LPCTSTR>(m_dialogTitle.getStr());
170 }
171 
172 //------------------------------------------------------------------------
173 //
174 //------------------------------------------------------------------------
175 
176 void CFileOpenDialog::setFilter(const rtl::OUString& aFilter)
177 {
178     // Format is like
179     // "*.TXT" or multiple separate by ';' like "*.TXT;*.DOC;*.SXW"
180     // Do not include spaces in the pattern string
181     m_filterBuffer.ensureCapacity(aFilter.getLength());
182     m_filterBuffer.setLength(0);
183     m_filterBuffer.append(aFilter);
184     m_ofn.lpstrFilter = reinterpret_cast<LPCTSTR>(m_filterBuffer.getStr());
185 }
186 
187 //------------------------------------------------------------------------
188 //
189 //------------------------------------------------------------------------
190 
191 bool CFileOpenDialog::setFilterIndex(sal_uInt32 aIndex)
192 {
193     OSL_ASSERT(aIndex > 0);
194     m_ofn.nFilterIndex = aIndex;
195     return sal_True;
196 }
197 
198 //------------------------------------------------------------------------
199 //
200 //------------------------------------------------------------------------
201 
202 sal_uInt32 CFileOpenDialog::getSelectedFilterIndex() const
203 {
204     return m_ofn.nFilterIndex;
205 }
206 
207 //------------------------------------------------------------------------
208 //
209 //------------------------------------------------------------------------
210 
211 void SAL_CALL CFileOpenDialog::setDefaultName(const rtl::OUString& aName)
212 {
213     m_fileNameBuffer.setLength(0);
214     m_fileNameBuffer.append(aName);
215     m_ofn.lpstrFile = reinterpret_cast<LPTSTR>(const_cast<sal_Unicode*>(m_fileNameBuffer.getStr()));
216 }
217 
218 //------------------------------------------------------------------------
219 //
220 //------------------------------------------------------------------------
221 
222 void SAL_CALL CFileOpenDialog::setDisplayDirectory(const rtl::OUString& aDirectory)
223 {
224     m_displayDirectory = aDirectory;
225     m_ofn.lpstrInitialDir = reinterpret_cast<LPCTSTR>(m_displayDirectory.getStr());
226 }
227 
228 //------------------------------------------------------------------------
229 //
230 //------------------------------------------------------------------------
231 
232 rtl::OUString SAL_CALL CFileOpenDialog::getLastDisplayDirectory() const
233 {
234     return m_displayDirectory;
235 }
236 
237 //------------------------------------------------------------------------
238 //
239 //------------------------------------------------------------------------
240 
241 rtl::OUString SAL_CALL CFileOpenDialog::getFullFileName() const
242 {
243     return rtl::OUString(m_fileNameBuffer.getStr(),
244         _wcslenex(m_fileNameBuffer.getStr()));
245 }
246 
247 //------------------------------------------------------------------------
248 //
249 //------------------------------------------------------------------------
250 
251 rtl::OUString SAL_CALL CFileOpenDialog::getFileName() const
252 {
253     return rtl::OUString(m_fileTitleBuffer);
254 }
255 
256 //------------------------------------------------------------------------
257 //
258 //------------------------------------------------------------------------
259 
260 rtl::OUString CFileOpenDialog::getFileExtension()
261 {
262     if (m_ofn.nFileExtension)
263         return rtl::OUString(m_fileNameBuffer.getStr() + m_ofn.nFileExtension,
264             rtl_ustr_getLength(m_fileNameBuffer.getStr() + m_ofn.nFileExtension));
265 
266     return rtl::OUString();
267 }
268 
269 //------------------------------------------------------------------------
270 //
271 //------------------------------------------------------------------------
272 
273 void CFileOpenDialog::setDefaultFileExtension(const rtl::OUString& aExtension)
274 {
275     m_defaultExtension = aExtension;
276     m_ofn.lpstrDefExt  = reinterpret_cast<LPCTSTR>(m_defaultExtension.getStr());
277 }
278 
279 //------------------------------------------------------------------------
280 //
281 //------------------------------------------------------------------------
282 
283 void SAL_CALL CFileOpenDialog::setMultiSelectionMode(bool bMode)
284 {
285     if (bMode)
286         m_ofn.Flags |= OFN_ALLOWMULTISELECT;
287     else
288         m_ofn.Flags &= ~OFN_ALLOWMULTISELECT;
289 }
290 
291 //------------------------------------------------------------------------
292 //
293 //------------------------------------------------------------------------
294 
295 bool SAL_CALL CFileOpenDialog::getMultiSelectionMode() const
296 {
297     return ((m_ofn.Flags & OFN_ALLOWMULTISELECT) > 0);
298 }
299 
300 //------------------------------------------------------------------------
301 //
302 //------------------------------------------------------------------------
303 
304 sal_Int16 SAL_CALL CFileOpenDialog::doModal()
305 {
306     sal_Int16 nRC = -1;
307 
308     // pre-processing
309     if (preModal())
310     {
311         bool bRet;
312 
313         if (m_bFileOpenDialog)
314             bRet = m_GetFileNameWrapper.getOpenFileName(
315                 reinterpret_cast<LPOPENFILENAME>(&m_ofn));
316         else
317             bRet = m_GetFileNameWrapper.getSaveFileName(
318                 reinterpret_cast<LPOPENFILENAME>(&m_ofn));
319 
320         nRC = 1;
321 
322         if (!bRet)
323             nRC = (0 == m_GetFileNameWrapper.commDlgExtendedError()) ? 0 : -1;
324 
325         // post-processing
326         postModal(nRC);
327     }
328 
329     return nRC;
330 }
331 
332 //------------------------------------------------------------------------
333 //
334 //------------------------------------------------------------------------
335 
336 sal_uInt32 SAL_CALL CFileOpenDialog::getLastDialogError() const
337 {
338     return CommDlgExtendedError();
339 }
340 
341 //------------------------------------------------------------------------
342 //
343 //------------------------------------------------------------------------
344 
345 bool SAL_CALL CFileOpenDialog::preModal()
346 {
347     return sal_True;
348 }
349 
350 //------------------------------------------------------------------------
351 //
352 //------------------------------------------------------------------------
353 
354 void SAL_CALL CFileOpenDialog::postModal(sal_Int16 nDialogResult)
355 {
356     OSL_ASSERT((-1 <= nDialogResult) && (nDialogResult <= 1));
357 
358     if (1 == nDialogResult)
359     {
360         // Attention: assuming that nFileOffset is always greater 0 because under
361         // Windows there is always a drive letter or a server in a complete path
362         // the OPENFILENAME docu never says that nFileOffset can be 0
363         m_displayDirectory = rtl::OUString(reinterpret_cast<const sal_Unicode*>(m_ofn.lpstrFile),m_ofn.nFileOffset);
364     }
365 }
366 
367 //------------------------------------------------------------------------
368 //
369 //------------------------------------------------------------------------
370 
371 rtl::OUString SAL_CALL CFileOpenDialog::getCurrentFilePath() const
372 {
373     OSL_ASSERT(IsWindow(m_hwndFileOpenDlg));
374 
375     LPARAM nLen = SendMessage(
376         m_hwndFileOpenDlg,
377         CDM_GETFILEPATH,
378         m_helperBuffer.getCapacity(),
379         reinterpret_cast<LPARAM>(m_helperBuffer.getStr()));
380 
381     if (nLen > 0)
382     {
383         m_helperBuffer.setLength((nLen * sizeof(sal_Unicode)) - 1);
384         return rtl::OUString(m_helperBuffer);
385     }
386     return rtl::OUString();
387 }
388 
389 //------------------------------------------------------------------------
390 //
391 //------------------------------------------------------------------------
392 
393 rtl::OUString SAL_CALL CFileOpenDialog::getCurrentFolderPath() const
394 {
395     OSL_ASSERT(IsWindow(m_hwndFileOpenDlg));
396 
397     LPARAM nLen = SendMessage(
398         m_hwndFileOpenDlg,
399         CDM_GETFOLDERPATH,
400         m_helperBuffer.getCapacity(),
401         reinterpret_cast<LPARAM>(m_helperBuffer.getStr()));
402 
403     if (nLen > 0)
404     {
405         m_helperBuffer.setLength((nLen * sizeof(sal_Unicode)) - 1);
406         return rtl::OUString(m_helperBuffer);
407     }
408     return rtl::OUString();
409 }
410 
411 //------------------------------------------------------------------------
412 //
413 //------------------------------------------------------------------------
414 
415 rtl::OUString SAL_CALL CFileOpenDialog::getCurrentFileName() const
416 {
417     OSL_ASSERT(IsWindow(m_hwndFileOpenDlg));
418 
419     LPARAM nLen = SendMessage(
420         m_hwndFileOpenDlg,
421         CDM_GETSPEC,
422         m_helperBuffer.getCapacity(),
423         reinterpret_cast<LPARAM>(m_helperBuffer.getStr()));
424 
425     if (nLen > 0)
426     {
427         m_helperBuffer.setLength((nLen * sizeof(sal_Unicode)) - 1);
428         return rtl::OUString(m_helperBuffer);
429     }
430     return rtl::OUString();
431 }
432 
433 //------------------------------------------------------------------------
434 //
435 //------------------------------------------------------------------------
436 
437 sal_uInt32 SAL_CALL CFileOpenDialog::onShareViolation(const rtl::OUString&)
438 {
439     return 0;
440 }
441 
442 //------------------------------------------------------------------------
443 //
444 //------------------------------------------------------------------------
445 
446 sal_uInt32 SAL_CALL CFileOpenDialog::onFileOk()
447 {
448     return 0;
449 }
450 
451 //------------------------------------------------------------------------
452 //
453 //------------------------------------------------------------------------
454 
455 void SAL_CALL CFileOpenDialog::onSelChanged(HWND)
456 {
457 }
458 
459 //------------------------------------------------------------------------
460 //
461 //------------------------------------------------------------------------
462 
463 void SAL_CALL CFileOpenDialog::onHelp()
464 {
465 }
466 
467 //------------------------------------------------------------------------
468 //
469 //------------------------------------------------------------------------
470 
471 void SAL_CALL CFileOpenDialog::onInitDone()
472 {
473     centerPositionToParent();
474 }
475 
476 //------------------------------------------------------------------------
477 //
478 //------------------------------------------------------------------------
479 
480 void SAL_CALL CFileOpenDialog::onFolderChanged()
481 {
482 }
483 
484 //------------------------------------------------------------------------
485 //
486 //------------------------------------------------------------------------
487 
488 void SAL_CALL CFileOpenDialog::onTypeChanged(sal_uInt32)
489 {
490 }
491 
492 //------------------------------------------------------------------------
493 //
494 //------------------------------------------------------------------------
495 
496 sal_uInt32 SAL_CALL CFileOpenDialog::onCtrlCommand(HWND, sal_uInt16, sal_uInt16)
497 {
498     return 0;
499 }
500 
501 //------------------------------------------------------------------------
502 //
503 //------------------------------------------------------------------------
504 
505 sal_uInt32 SAL_CALL CFileOpenDialog::onWMNotify( HWND, LPOFNOTIFY lpOfNotify )
506 {
507     switch(lpOfNotify->hdr.code)
508     {
509     case CDN_SHAREVIOLATION:
510         return onShareViolation(reinterpret_cast<const sal_Unicode*>(lpOfNotify->pszFile));
511 
512     case CDN_FILEOK:
513         return onFileOk();
514 
515     case CDN_SELCHANGE:
516         onSelChanged(lpOfNotify->hdr.hwndFrom);
517         break;
518 
519     case CDN_HELP:
520         onHelp();
521         break;
522 
523     case CDN_INITDONE:
524         onInitDone();
525         break;
526 
527     case CDN_FOLDERCHANGE:
528         onFolderChanged();
529         break;
530 
531     case CDN_TYPECHANGE:
532         m_ofn.nFilterIndex = lpOfNotify->lpOFN->nFilterIndex;
533         onTypeChanged(lpOfNotify->lpOFN->nFilterIndex);
534         break;
535     }
536 
537     return 0;
538 }
539 
540 //------------------------------------------------------------------------
541 //
542 //------------------------------------------------------------------------
543 
544 void SAL_CALL CFileOpenDialog::handleInitDialog(HWND hwndDlg, HWND hwndChild)
545 {
546     m_hwndFileOpenDlg      = hwndDlg;
547     m_hwndFileOpenDlgChild = hwndChild;
548 
549     OSL_ASSERT(GetParent(hwndChild) == hwndDlg);
550 
551     // calling virtual function which the
552     // client can overload
553     onInitDialog(hwndDlg);
554 }
555 
556 //------------------------------------------------------------------------
557 //
558 //------------------------------------------------------------------------
559 
560 unsigned int CALLBACK CFileOpenDialog::ofnHookProc(
561     HWND hChildDlg, unsigned int uiMsg, WPARAM wParam, LPARAM lParam)
562 {
563     HWND hwndDlg = GetParent(hChildDlg);
564     CFileOpenDialog* pImpl = NULL;
565 
566     switch( uiMsg )
567     {
568     case WM_INITDIALOG:
569         {
570             _LPOPENFILENAME lpofn = reinterpret_cast<_LPOPENFILENAME>(lParam);
571             pImpl = reinterpret_cast<CFileOpenDialog*>(lpofn->lCustData);
572             OSL_ASSERT(pImpl);
573 
574             // subclass the base dialog for WM_NCDESTROY processing
575             pImpl->m_pfnBaseDlgProc =
576                 reinterpret_cast<WNDPROC>(
577                     SetWindowLong(
578                         hwndDlg,
579                         GWL_WNDPROC,
580                         reinterpret_cast<LONG>(CFileOpenDialog::BaseDlgProc)));
581             // connect the instance handle to the window
582             SetProp(hwndDlg, CURRENT_INSTANCE, pImpl);
583             pImpl->handleInitDialog(hwndDlg, hChildDlg);
584         }
585         return 0;
586 
587     case WM_NOTIFY:
588         {
589             pImpl = getCurrentInstance(hwndDlg);
590             return pImpl->onWMNotify(
591                 hChildDlg, reinterpret_cast<LPOFNOTIFY>(lParam));
592         }
593 
594     case WM_COMMAND:
595         {
596             pImpl = getCurrentInstance(hwndDlg);
597             OSL_ASSERT(pImpl);
598 
599             return pImpl->onCtrlCommand(
600                 hChildDlg, LOWORD(wParam), HIWORD(lParam));
601         }
602     }
603 
604     return 0;
605 }
606 
607 //------------------------------------------------------------------------
608 //
609 //------------------------------------------------------------------------
610 
611 LRESULT CALLBACK CFileOpenDialog::BaseDlgProc(
612     HWND hWnd, UINT wMessage, WPARAM wParam, LPARAM lParam)
613 {
614     CFileOpenDialog* pImpl = 0;
615 
616     if (WM_NCDESTROY == wMessage)
617     {
618         pImpl = reinterpret_cast<CFileOpenDialog*>(
619             RemoveProp(hWnd,CURRENT_INSTANCE));
620 
621         SetWindowLong(hWnd, GWL_WNDPROC,
622             reinterpret_cast<LONG>(pImpl->m_pfnBaseDlgProc));
623     }
624     else
625     {
626         pImpl = getCurrentInstance(hWnd);
627     }
628 
629     OSL_ASSERT(pImpl);
630 
631     return CallWindowProc(
632         reinterpret_cast<WNDPROC>(pImpl->m_pfnBaseDlgProc),
633         hWnd,wMessage,wParam,lParam);
634 }
635 
636 //------------------------------------------------------------------------
637 //
638 //------------------------------------------------------------------------
639 
640 CFileOpenDialog* SAL_CALL CFileOpenDialog::getCurrentInstance(HWND hwnd)
641 {
642     OSL_ASSERT(IsWindow( hwnd));
643     return reinterpret_cast<CFileOpenDialog*>(
644         GetProp(hwnd, CURRENT_INSTANCE));
645 }
646 
647 //------------------------------------------------------------------------
648 //
649 //------------------------------------------------------------------------
650 
651 void SAL_CALL CFileOpenDialog::centerPositionToParent() const
652 {
653     OSL_PRECOND(IsWindow(m_hwndFileOpenDlg), "no dialog window, call method only after or in onInitDone");
654 
655     HWND hwndParent = m_ofn.hwndOwner;
656 
657     if (!IsWindow(hwndParent))
658         hwndParent = GetDesktopWindow();
659 
660     OSL_ASSERT(IsWindow(hwndParent));
661 
662     RECT rcPar;
663     GetWindowRect(hwndParent, &rcPar);
664 
665     RECT rcDlg;
666     GetWindowRect(m_hwndFileOpenDlg, &rcDlg);
667 
668     int lDlgW = rcDlg.right  - rcDlg.left;
669     int lDlgH = rcDlg.bottom - rcDlg.top;
670 
671     int x = (rcPar.left + rcPar.right  - lDlgW) / 2;
672     int y = (rcPar.top  + rcPar.bottom - lDlgH) / 2;
673 
674     HDC hdc = GetDC(m_hwndFileOpenDlg);
675 
676     int hResol = GetDeviceCaps(hdc, HORZRES);
677     int vResol = GetDeviceCaps(hdc, VERTRES);
678 
679     ReleaseDC(m_hwndFileOpenDlg, hdc);
680 
681     if (x < 0)
682         x = 0;
683     else if ((x + lDlgW) > hResol)
684         x = hResol - lDlgW;
685 
686     if (y < 0)
687         y = 0;
688     else if ((y + lDlgH) > vResol)
689         y = vResol - lDlgH;
690 
691     SetWindowPos(
692         m_hwndFileOpenDlg,
693         NULL, x, y, 0, 0,
694         SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE );
695 }
696