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