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