llfilepicker.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:32k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file llfilepicker.cpp
  3.  * @brief OS-specific file picker
  4.  *
  5.  * $LicenseInfo:firstyear=2001&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2001-2010, Linden Research, Inc.
  8.  * 
  9.  * Second Life Viewer Source Code
  10.  * The source code in this file ("Source Code") is provided by Linden Lab
  11.  * to you under the terms of the GNU General Public License, version 2.0
  12.  * ("GPL"), unless you have obtained a separate licensing agreement
  13.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  14.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16.  * 
  17.  * There are special exceptions to the terms and conditions of the GPL as
  18.  * it is applied to this Source Code. View the full text of the exception
  19.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  20.  * online at
  21.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22.  * 
  23.  * By copying, modifying or distributing this software, you acknowledge
  24.  * that you have read and understood your obligations described above,
  25.  * and agree to abide by those obligations.
  26.  * 
  27.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29.  * COMPLETENESS OR PERFORMANCE.
  30.  * $/LicenseInfo$
  31.  */
  32. #include "llviewerprecompiledheaders.h"
  33. #include "llfilepicker.h"
  34. #include "llworld.h"
  35. #include "llviewerwindow.h"
  36. #include "llkeyboard.h"
  37. #include "lldir.h"
  38. #include "llframetimer.h"
  39. #include "lltrans.h"
  40. #include "llwindow.h" // beforeDialog()
  41. #if LL_SDL
  42. #include "llwindowsdl.h" // for some X/GTK utils to help with filepickers
  43. #endif // LL_SDL
  44. //
  45. // Globals
  46. //
  47. LLFilePicker LLFilePicker::sInstance;
  48. #if LL_WINDOWS
  49. #define SOUND_FILTER L"Sounds (*.wav)*.wav"
  50. #define IMAGE_FILTER L"Images (*.tga; *.bmp; *.jpg; *.jpeg; *.png)*.tga;*.bmp;*.jpg;*.jpeg;*.png"
  51. #define ANIM_FILTER L"Animations (*.bvh)*.bvh"
  52. #ifdef _CORY_TESTING
  53. #define GEOMETRY_FILTER L"SL Geometry (*.slg)*.slg"
  54. #endif
  55. #define XML_FILTER L"XML files (*.xml)*.xml"
  56. #define SLOBJECT_FILTER L"Objects (*.slobject)*.slobject"
  57. #define RAW_FILTER L"RAW files (*.raw)*.raw"
  58. #endif
  59. //
  60. // Implementation
  61. //
  62. LLFilePicker::LLFilePicker()
  63. : mCurrentFile(0),
  64.   mLocked(false)
  65. {
  66. reset();
  67. #if LL_WINDOWS
  68. mOFN.lStructSize = sizeof(OPENFILENAMEW);
  69. mOFN.hwndOwner = NULL;  // Set later
  70. mOFN.hInstance = NULL;
  71. mOFN.lpstrCustomFilter = NULL;
  72. mOFN.nMaxCustFilter = 0;
  73. mOFN.lpstrFile = NULL; // set in open and close
  74. mOFN.nMaxFile = LL_MAX_PATH;
  75. mOFN.lpstrFileTitle = NULL;
  76. mOFN.nMaxFileTitle = 0;
  77. mOFN.lpstrInitialDir = NULL;
  78. mOFN.lpstrTitle = NULL;
  79. mOFN.Flags = 0; // set in open and close
  80. mOFN.nFileOffset = 0;
  81. mOFN.nFileExtension = 0;
  82. mOFN.lpstrDefExt = NULL;
  83. mOFN.lCustData = 0L;
  84. mOFN.lpfnHook = NULL;
  85. mOFN.lpTemplateName = NULL;
  86. mFilesW[0] = '';
  87. #endif
  88. #if LL_DARWIN
  89. memset(&mNavOptions, 0, sizeof(mNavOptions));
  90. OSStatus error = NavGetDefaultDialogCreationOptions(&mNavOptions);
  91. if (error == noErr)
  92. {
  93. mNavOptions.modality = kWindowModalityAppModal;
  94. }
  95. #endif
  96. }
  97. LLFilePicker::~LLFilePicker()
  98. {
  99. // nothing
  100. }
  101. const std::string LLFilePicker::getFirstFile()
  102. {
  103. mCurrentFile = 0;
  104. return getNextFile();
  105. }
  106. const std::string LLFilePicker::getNextFile()
  107. {
  108. if (mCurrentFile >= getFileCount())
  109. {
  110. mLocked = false;
  111. return std::string();
  112. }
  113. else
  114. {
  115. return mFiles[mCurrentFile++];
  116. }
  117. }
  118. const std::string LLFilePicker::getCurFile()
  119. {
  120. if (mCurrentFile >= getFileCount())
  121. {
  122. mLocked = false;
  123. return std::string();
  124. }
  125. else
  126. {
  127. return mFiles[mCurrentFile];
  128. }
  129. }
  130. void LLFilePicker::reset()
  131. {
  132. mLocked = false;
  133. mFiles.clear();
  134. mCurrentFile = 0;
  135. }
  136. #if LL_WINDOWS
  137. BOOL LLFilePicker::setupFilter(ELoadFilter filter)
  138. {
  139. BOOL res = TRUE;
  140. switch (filter)
  141. {
  142. case FFLOAD_ALL:
  143. mOFN.lpstrFilter = L"All Files (*.*)*.*" 
  144. SOUND_FILTER 
  145. IMAGE_FILTER 
  146. ANIM_FILTER 
  147. L"";
  148. break;
  149. case FFLOAD_WAV:
  150. mOFN.lpstrFilter = SOUND_FILTER 
  151. L"";
  152. break;
  153. case FFLOAD_IMAGE:
  154. mOFN.lpstrFilter = IMAGE_FILTER 
  155. L"";
  156. break;
  157. case FFLOAD_ANIM:
  158. mOFN.lpstrFilter = ANIM_FILTER 
  159. L"";
  160. break;
  161. #ifdef _CORY_TESTING
  162. case FFLOAD_GEOMETRY:
  163. mOFN.lpstrFilter = GEOMETRY_FILTER 
  164. L"";
  165. break;
  166. #endif
  167. case FFLOAD_XML:
  168. mOFN.lpstrFilter = XML_FILTER 
  169. L"";
  170. break;
  171. case FFLOAD_SLOBJECT:
  172. mOFN.lpstrFilter = SLOBJECT_FILTER 
  173. L"";
  174. break;
  175. case FFLOAD_RAW:
  176. mOFN.lpstrFilter = RAW_FILTER 
  177. L"";
  178. break;
  179. default:
  180. res = FALSE;
  181. break;
  182. }
  183. return res;
  184. }
  185. BOOL LLFilePicker::getOpenFile(ELoadFilter filter)
  186. {
  187. if( mLocked )
  188. {
  189. return FALSE;
  190. }
  191. BOOL success = FALSE;
  192. // don't provide default file selection
  193. mFilesW[0] = '';
  194. mOFN.hwndOwner = (HWND)gViewerWindow->getPlatformWindow();
  195. mOFN.lpstrFile = mFilesW;
  196. mOFN.nMaxFile = SINGLE_FILENAME_BUFFER_SIZE;
  197. mOFN.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR ;
  198. mOFN.nFilterIndex = 1;
  199. setupFilter(filter);
  200. // Modal, so pause agent
  201. send_agent_pause();
  202. reset();
  203. // NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!!
  204. success = GetOpenFileName(&mOFN);
  205. if (success)
  206. {
  207. std::string filename = utf16str_to_utf8str(llutf16string(mFilesW));
  208. mFiles.push_back(filename);
  209. }
  210. send_agent_resume();
  211. // Account for the fact that the app has been stalled.
  212. LLFrameTimer::updateFrameTime();
  213. return success;
  214. }
  215. BOOL LLFilePicker::getMultipleOpenFiles(ELoadFilter filter)
  216. {
  217. if( mLocked )
  218. {
  219. return FALSE;
  220. }
  221. BOOL success = FALSE;
  222. // don't provide default file selection
  223. mFilesW[0] = '';
  224. mOFN.hwndOwner = (HWND)gViewerWindow->getPlatformWindow();
  225. mOFN.lpstrFile = mFilesW;
  226. mOFN.nFilterIndex = 1;
  227. mOFN.nMaxFile = FILENAME_BUFFER_SIZE;
  228. mOFN.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR |
  229. OFN_EXPLORER | OFN_ALLOWMULTISELECT;
  230. setupFilter(filter);
  231. reset();
  232. // Modal, so pause agent
  233. send_agent_pause();
  234. // NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!!
  235. success = GetOpenFileName(&mOFN); // pauses until ok or cancel.
  236. if( success )
  237. {
  238. // The getopenfilename api doesn't tell us if we got more than
  239. // one file, so we have to test manually by checking string
  240. // lengths.
  241. if( wcslen(mOFN.lpstrFile) > mOFN.nFileOffset ) /*Flawfinder: ignore*/
  242. {
  243. std::string filename = utf16str_to_utf8str(llutf16string(mFilesW));
  244. mFiles.push_back(filename);
  245. }
  246. else
  247. {
  248. mLocked = true;
  249. WCHAR* tptrw = mFilesW;
  250. std::string dirname;
  251. while(1)
  252. {
  253. if (*tptrw == 0 && *(tptrw+1) == 0) // double ''
  254. break;
  255. if (*tptrw == 0)
  256. tptrw++; // shouldn't happen?
  257. std::string filename = utf16str_to_utf8str(llutf16string(tptrw));
  258. if (dirname.empty())
  259. dirname = filename + "\";
  260. else
  261. mFiles.push_back(dirname + filename);
  262. tptrw += filename.size();
  263. }
  264. }
  265. }
  266. send_agent_resume();
  267. // Account for the fact that the app has been stalled.
  268. LLFrameTimer::updateFrameTime();
  269. return success;
  270. }
  271. BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename)
  272. {
  273. if( mLocked )
  274. {
  275. return FALSE;
  276. }
  277. BOOL success = FALSE;
  278. mOFN.lpstrFile = mFilesW;
  279. if (!filename.empty())
  280. {
  281. llutf16string tstring = utf8str_to_utf16str(filename);
  282. wcsncpy(mFilesW, tstring.c_str(), FILENAME_BUFFER_SIZE); } /*Flawfinder: ignore*/
  283. else
  284. {
  285. mFilesW[0] = '';
  286. }
  287. mOFN.hwndOwner = (HWND)gViewerWindow->getPlatformWindow();
  288. switch( filter )
  289. {
  290. case FFSAVE_ALL:
  291. mOFN.lpstrDefExt = NULL;
  292. mOFN.lpstrFilter =
  293. L"All Files (*.*)*.*" 
  294. L"WAV Sounds (*.wav)*.wav" 
  295. L"Targa, Bitmap Images (*.tga; *.bmp)*.tga;*.bmp" 
  296. L"";
  297. break;
  298. case FFSAVE_WAV:
  299. if (filename.empty())
  300. {
  301. wcsncpy( mFilesW,L"untitled.wav", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/
  302. }
  303. mOFN.lpstrDefExt = L"wav";
  304. mOFN.lpstrFilter =
  305. L"WAV Sounds (*.wav)*.wav" 
  306. L"";
  307. break;
  308. case FFSAVE_TGA:
  309. if (filename.empty())
  310. {
  311. wcsncpy( mFilesW,L"untitled.tga", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/
  312. }
  313. mOFN.lpstrDefExt = L"tga";
  314. mOFN.lpstrFilter =
  315. L"Targa Images (*.tga)*.tga" 
  316. L"";
  317. break;
  318. case FFSAVE_BMP:
  319. if (filename.empty())
  320. {
  321. wcsncpy( mFilesW,L"untitled.bmp", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/
  322. }
  323. mOFN.lpstrDefExt = L"bmp";
  324. mOFN.lpstrFilter =
  325. L"Bitmap Images (*.bmp)*.bmp" 
  326. L"";
  327. break;
  328. case FFSAVE_PNG:
  329. if (filename.empty())
  330. {
  331. wcsncpy( mFilesW,L"untitled.png", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/
  332. }
  333. mOFN.lpstrDefExt = L"png";
  334. mOFN.lpstrFilter =
  335. L"PNG Images (*.png)*.png" 
  336. L"";
  337. break;
  338. case FFSAVE_JPEG:
  339. if (filename.empty())
  340. {
  341. wcsncpy( mFilesW,L"untitled.jpeg", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/
  342. }
  343. mOFN.lpstrDefExt = L"jpeg";
  344. mOFN.lpstrFilter =
  345. L"JPEG Images (*.jpeg)*.jpeg" 
  346. L"";
  347. break;
  348. case FFSAVE_AVI:
  349. if (filename.empty())
  350. {
  351. wcsncpy( mFilesW,L"untitled.avi", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/
  352. }
  353. mOFN.lpstrDefExt = L"avi";
  354. mOFN.lpstrFilter =
  355. L"AVI Movie File (*.avi)*.avi" 
  356. L"";
  357. break;
  358. case FFSAVE_ANIM:
  359. if (filename.empty())
  360. {
  361. wcsncpy( mFilesW,L"untitled.xaf", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/
  362. }
  363. mOFN.lpstrDefExt = L"xaf";
  364. mOFN.lpstrFilter =
  365. L"XAF Anim File (*.xaf)*.xaf" 
  366. L"";
  367. break;
  368. #ifdef _CORY_TESTING
  369. case FFSAVE_GEOMETRY:
  370. if (filename.empty())
  371. {
  372. wcsncpy( mFilesW,L"untitled.slg", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/
  373. }
  374. mOFN.lpstrDefExt = L"slg";
  375. mOFN.lpstrFilter =
  376. L"SLG SL Geometry File (*.slg)*.slg" 
  377. L"";
  378. break;
  379. #endif
  380. case FFSAVE_XML:
  381. if (filename.empty())
  382. {
  383. wcsncpy( mFilesW,L"untitled.xml", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/
  384. }
  385. mOFN.lpstrDefExt = L"xml";
  386. mOFN.lpstrFilter =
  387. L"XML File (*.xml)*.xml" 
  388. L"";
  389. break;
  390. case FFSAVE_COLLADA:
  391. if (filename.empty())
  392. {
  393. wcsncpy( mFilesW,L"untitled.collada", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/
  394. }
  395. mOFN.lpstrDefExt = L"collada";
  396. mOFN.lpstrFilter =
  397. L"COLLADA File (*.collada)*.collada" 
  398. L"";
  399. break;
  400. case FFSAVE_RAW:
  401. if (filename.empty())
  402. {
  403. wcsncpy( mFilesW,L"untitled.raw", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/
  404. }
  405. mOFN.lpstrDefExt = L"raw";
  406. mOFN.lpstrFilter = RAW_FILTER 
  407. L"";
  408. break;
  409. case FFSAVE_J2C:
  410. if (filename.empty())
  411. {
  412. wcsncpy( mFilesW,L"untitled.j2c", FILENAME_BUFFER_SIZE);
  413. }
  414. mOFN.lpstrDefExt = L"j2c";
  415. mOFN.lpstrFilter =
  416. L"Compressed Images (*.j2c)*.j2c" 
  417. L"";
  418. break;
  419. default:
  420. return FALSE;
  421. }
  422.  
  423. mOFN.nMaxFile = SINGLE_FILENAME_BUFFER_SIZE;
  424. mOFN.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST;
  425. reset();
  426. // Modal, so pause agent
  427. send_agent_pause();
  428. {
  429. // NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!!
  430. success = GetSaveFileName(&mOFN);
  431. if (success)
  432. {
  433. std::string filename = utf16str_to_utf8str(llutf16string(mFilesW));
  434. mFiles.push_back(filename);
  435. }
  436. gKeyboard->resetKeys();
  437. }
  438. send_agent_resume();
  439. // Account for the fact that the app has been stalled.
  440. LLFrameTimer::updateFrameTime();
  441. return success;
  442. }
  443. #elif LL_DARWIN
  444. Boolean LLFilePicker::navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode)
  445. {
  446. Boolean result = true;
  447. ELoadFilter filter = *((ELoadFilter*) callBackUD);
  448. OSStatus error = noErr;
  449. if (filterMode == kNavFilteringBrowserList && filter != FFLOAD_ALL && (theItem->descriptorType == typeFSRef || theItem->descriptorType == typeFSS))
  450. {
  451. // navInfo is only valid for typeFSRef and typeFSS
  452. NavFileOrFolderInfo *navInfo = (NavFileOrFolderInfo*) info;
  453. if (!navInfo->isFolder)
  454. {
  455. AEDesc desc;
  456. error = AECoerceDesc(theItem, typeFSRef, &desc);
  457. if (error == noErr)
  458. {
  459. FSRef fileRef;
  460. error = AEGetDescData(&desc, &fileRef, sizeof(fileRef));
  461. if (error == noErr)
  462. {
  463. LSItemInfoRecord fileInfo;
  464. error = LSCopyItemInfoForRef(&fileRef, kLSRequestExtension | kLSRequestTypeCreator, &fileInfo);
  465. if (error == noErr)
  466. {
  467. if (filter == FFLOAD_IMAGE)
  468. {
  469. if (fileInfo.filetype != 'JPEG' && fileInfo.filetype != 'JPG ' && 
  470. fileInfo.filetype != 'BMP ' && fileInfo.filetype != 'TGA ' &&
  471. fileInfo.filetype != 'BMPf' && fileInfo.filetype != 'TPIC' &&
  472. fileInfo.filetype != 'PNG ' &&
  473. (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("jpeg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
  474. CFStringCompare(fileInfo.extension, CFSTR("jpg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
  475. CFStringCompare(fileInfo.extension, CFSTR("bmp"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
  476. CFStringCompare(fileInfo.extension, CFSTR("tga"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
  477. CFStringCompare(fileInfo.extension, CFSTR("png"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
  478. )
  479. {
  480. result = false;
  481. }
  482. }
  483. else if (filter == FFLOAD_WAV)
  484. {
  485. if (fileInfo.filetype != 'WAVE' && fileInfo.filetype != 'WAV ' && 
  486. (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("wave"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && 
  487. CFStringCompare(fileInfo.extension, CFSTR("wav"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
  488. )
  489. {
  490. result = false;
  491. }
  492. }
  493. else if (filter == FFLOAD_ANIM)
  494. {
  495. if (fileInfo.filetype != 'BVH ' && 
  496. (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("bvh"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
  497. )
  498. {
  499. result = false;
  500. }
  501. }
  502. #ifdef _CORY_TESTING
  503. else if (filter == FFLOAD_GEOMETRY)
  504. {
  505. if (fileInfo.filetype != 'SLG ' && 
  506. (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("slg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
  507. )
  508. {
  509. result = false;
  510. }
  511. }
  512. #endif
  513. else if (filter == FFLOAD_SLOBJECT)
  514. {
  515. llwarns << "*** navOpenFilterProc: FFLOAD_SLOBJECT NOT IMPLEMENTED ***" << llendl;
  516. }
  517. else if (filter == FFLOAD_RAW)
  518. {
  519. if (fileInfo.filetype != '????' && 
  520. (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("raw"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
  521. )
  522. {
  523. result = false;
  524. }
  525. }
  526. if (fileInfo.extension)
  527. {
  528. CFRelease(fileInfo.extension);
  529. }
  530. }
  531. }
  532. AEDisposeDesc(&desc);
  533. }
  534. }
  535. }
  536. return result;
  537. }
  538. OSStatus LLFilePicker::doNavChooseDialog(ELoadFilter filter)
  539. {
  540. OSStatus error = noErr;
  541. NavDialogRef navRef = NULL;
  542. NavReplyRecord navReply;
  543. memset(&navReply, 0, sizeof(navReply));
  544. // NOTE: we are passing the address of a local variable here.  
  545. //   This is fine, because the object this call creates will exist for less than the lifetime of this function.
  546. //   (It is destroyed by NavDialogDispose() below.)
  547. error = NavCreateChooseFileDialog(&mNavOptions, NULL, NULL, NULL, navOpenFilterProc, (void*)(&filter), &navRef);
  548. gViewerWindow->mWindow->beforeDialog();
  549. if (error == noErr)
  550. error = NavDialogRun(navRef);
  551. gViewerWindow->mWindow->afterDialog();
  552. if (error == noErr)
  553. error = NavDialogGetReply(navRef, &navReply);
  554. if (navRef)
  555. NavDialogDispose(navRef);
  556. if (error == noErr && navReply.validRecord)
  557. {
  558. SInt32 count = 0;
  559. SInt32 index;
  560. // AE indexes are 1 based...
  561. error = AECountItems(&navReply.selection, &count);
  562. for (index = 1; index <= count; index++)
  563. {
  564. FSRef fsRef;
  565. AEKeyword theAEKeyword;
  566. DescType typeCode;
  567. Size actualSize = 0;
  568. char path[MAX_PATH]; /*Flawfinder: ignore*/
  569. memset(&fsRef, 0, sizeof(fsRef));
  570. error = AEGetNthPtr(&navReply.selection, index, typeFSRef, &theAEKeyword, &typeCode, &fsRef, sizeof(fsRef), &actualSize);
  571. if (error == noErr)
  572. error = FSRefMakePath(&fsRef, (UInt8*) path, sizeof(path));
  573. if (error == noErr)
  574. mFiles.push_back(std::string(path));
  575. }
  576. }
  577. return error;
  578. }
  579. OSStatus LLFilePicker::doNavSaveDialog(ESaveFilter filter, const std::string& filename)
  580. {
  581. OSStatus error = noErr;
  582. NavDialogRef navRef = NULL;
  583. NavReplyRecord navReply;
  584. memset(&navReply, 0, sizeof(navReply));
  585. // Setup the type, creator, and extension
  586. OSType type, creator;
  587. CFStringRef extension = NULL;
  588. switch (filter)
  589. {
  590. case FFSAVE_WAV:
  591. type = 'WAVE';
  592. creator = 'TVOD';
  593. extension = CFSTR(".wav");
  594. break;
  595. case FFSAVE_TGA:
  596. type = 'TPIC';
  597. creator = 'prvw';
  598. extension = CFSTR(".tga");
  599. break;
  600. case FFSAVE_BMP:
  601. type = 'BMPf';
  602. creator = 'prvw';
  603. extension = CFSTR(".bmp");
  604. break;
  605. case FFSAVE_JPEG:
  606. type = 'JPEG';
  607. creator = 'prvw';
  608. extension = CFSTR(".jpeg");
  609. break;
  610. case FFSAVE_PNG:
  611. type = 'PNG ';
  612. creator = 'prvw';
  613. extension = CFSTR(".png");
  614. break;
  615. case FFSAVE_AVI:
  616. type = '????';
  617. creator = '????';
  618. extension = CFSTR(".mov");
  619. break;
  620. case FFSAVE_ANIM:
  621. type = '????';
  622. creator = '????';
  623. extension = CFSTR(".xaf");
  624. break;
  625. #ifdef _CORY_TESTING
  626. case FFSAVE_GEOMETRY:
  627. type = '????';
  628. creator = '????';
  629. extension = CFSTR(".slg");
  630. break;
  631. #endif
  632. case FFSAVE_RAW:
  633. type = '????';
  634. creator = '????';
  635. extension = CFSTR(".raw");
  636. break;
  637. case FFSAVE_J2C:
  638. type = '????';
  639. creator = 'prvw';
  640. extension = CFSTR(".j2c");
  641. break;
  642. case FFSAVE_ALL:
  643. default:
  644. type = '????';
  645. creator = '????';
  646. extension = CFSTR("");
  647. break;
  648. }
  649. // Create the dialog
  650. error = NavCreatePutFileDialog(&mNavOptions, type, creator, NULL, NULL, &navRef);
  651. if (error == noErr)
  652. {
  653. CFStringRef nameString = NULL;
  654. bool hasExtension = true;
  655. // Create a CFString of the initial file name
  656. if (!filename.empty())
  657. nameString = CFStringCreateWithCString(NULL, filename.c_str(), kCFStringEncodingUTF8);
  658. else
  659. nameString = CFSTR("Untitled");
  660. // Add the extension if one was not provided
  661. if (nameString && !CFStringHasSuffix(nameString, extension))
  662. {
  663. CFStringRef tempString = nameString;
  664. hasExtension = false;
  665. nameString = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@"), tempString, extension);
  666. CFRelease(tempString);
  667. }
  668. // Set the name in the dialog
  669. if (nameString)
  670. {
  671. error = NavDialogSetSaveFileName(navRef, nameString);
  672. CFRelease(nameString);
  673. }
  674. else
  675. {
  676. error = paramErr;
  677. }
  678. }
  679. gViewerWindow->mWindow->beforeDialog();
  680. // Run the dialog
  681. if (error == noErr)
  682. error = NavDialogRun(navRef);
  683. gViewerWindow->mWindow->afterDialog();
  684. if (error == noErr)
  685. error = NavDialogGetReply(navRef, &navReply);
  686. if (navRef)
  687. NavDialogDispose(navRef);
  688. if (error == noErr && navReply.validRecord)
  689. {
  690. SInt32 count = 0;
  691. // AE indexes are 1 based...
  692. error = AECountItems(&navReply.selection, &count);
  693. if (count > 0)
  694. {
  695. // Get the FSRef to the containing folder
  696. FSRef fsRef;
  697. AEKeyword theAEKeyword;
  698. DescType typeCode;
  699. Size actualSize = 0;
  700. memset(&fsRef, 0, sizeof(fsRef));
  701. error = AEGetNthPtr(&navReply.selection, 1, typeFSRef, &theAEKeyword, &typeCode, &fsRef, sizeof(fsRef), &actualSize);
  702. if (error == noErr)
  703. {
  704. char path[PATH_MAX]; /*Flawfinder: ignore*/
  705. char newFileName[SINGLE_FILENAME_BUFFER_SIZE]; /*Flawfinder: ignore*/
  706. error = FSRefMakePath(&fsRef, (UInt8*)path, PATH_MAX);
  707. if (error == noErr)
  708. {
  709. if (CFStringGetCString(navReply.saveFileName, newFileName, sizeof(newFileName), kCFStringEncodingUTF8))
  710. {
  711. mFiles.push_back(std::string(path) + "/" +  std::string(newFileName));
  712. }
  713. else
  714. {
  715. error = paramErr;
  716. }
  717. }
  718. else
  719. {
  720. error = paramErr;
  721. }
  722. }
  723. }
  724. }
  725. return error;
  726. }
  727. BOOL LLFilePicker::getOpenFile(ELoadFilter filter)
  728. {
  729. if( mLocked )
  730. return FALSE;
  731. BOOL success = FALSE;
  732. OSStatus error = noErr;
  733. reset();
  734. mNavOptions.optionFlags &= ~kNavAllowMultipleFiles;
  735. if(filter == FFLOAD_ALL) // allow application bundles etc. to be traversed; important for DEV-16869, but generally useful
  736. {
  737. // mNavOptions.optionFlags |= kNavAllowOpenPackages;
  738. mNavOptions.optionFlags |= kNavSupportPackages;
  739. }
  740. // Modal, so pause agent
  741. send_agent_pause();
  742. {
  743. error = doNavChooseDialog(filter);
  744. }
  745. send_agent_resume();
  746. if (error == noErr)
  747. {
  748. if (getFileCount())
  749. success = true;
  750. }
  751. // Account for the fact that the app has been stalled.
  752. LLFrameTimer::updateFrameTime();
  753. return success;
  754. }
  755. BOOL LLFilePicker::getMultipleOpenFiles(ELoadFilter filter)
  756. {
  757. if( mLocked )
  758. return FALSE;
  759. BOOL success = FALSE;
  760. OSStatus error = noErr;
  761. reset();
  762. mNavOptions.optionFlags |= kNavAllowMultipleFiles;
  763. // Modal, so pause agent
  764. send_agent_pause();
  765. {
  766. error = doNavChooseDialog(filter);
  767. }
  768. send_agent_resume();
  769. if (error == noErr)
  770. {
  771. if (getFileCount())
  772. success = true;
  773. if (getFileCount() > 1)
  774. mLocked = true;
  775. }
  776. // Account for the fact that the app has been stalled.
  777. LLFrameTimer::updateFrameTime();
  778. return success;
  779. }
  780. BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename)
  781. {
  782. if( mLocked )
  783. return FALSE;
  784. BOOL success = FALSE;
  785. OSStatus error = noErr;
  786. reset();
  787. mNavOptions.optionFlags &= ~kNavAllowMultipleFiles;
  788. // Modal, so pause agent
  789. send_agent_pause();
  790. {
  791. error = doNavSaveDialog(filter, filename);
  792. }
  793. send_agent_resume();
  794. if (error == noErr)
  795. {
  796. if (getFileCount())
  797. success = true;
  798. }
  799. // Account for the fact that the app has been stalled.
  800. LLFrameTimer::updateFrameTime();
  801. return success;
  802. }
  803. #elif LL_LINUX || LL_SOLARIS
  804. # if LL_GTK
  805. // static
  806. void LLFilePicker::add_to_selectedfiles(gpointer data, gpointer user_data)
  807. {
  808. // We need to run g_filename_to_utf8 in the user's locale
  809. std::string saved_locale(setlocale(LC_ALL, NULL));
  810. setlocale(LC_ALL, "");
  811. LLFilePicker* picker = (LLFilePicker*) user_data;
  812. GError *error = NULL;
  813. gchar* filename_utf8 = g_filename_to_utf8((gchar*)data,
  814.   -1, NULL, NULL, &error);
  815. if (error)
  816. {
  817. // *FIXME.
  818. // This condition should really be notified to the user, e.g.
  819. // through a message box.  Just logging it is inappropriate.
  820. // g_filename_display_name is ideal, but >= glib 2.6, so:
  821. // a hand-rolled hacky makeASCII which disallows control chars
  822. std::string display_name;
  823. for (const gchar *str = (const gchar *)data; *str; str++)
  824. {
  825. display_name += (char)((*str >= 0x20 && *str <= 0x7E) ? *str : '?');
  826. }
  827. llwarns << "g_filename_to_utf8 failed on "" << display_name << "": " << error->message << llendl;
  828. }
  829. if (filename_utf8)
  830. {
  831. picker->mFiles.push_back(std::string(filename_utf8));
  832. lldebugs << "ADDED FILE " << filename_utf8 << llendl;
  833. g_free(filename_utf8);
  834. }
  835. setlocale(LC_ALL, saved_locale.c_str());
  836. }
  837. // static
  838. void LLFilePicker::chooser_responder(GtkWidget *widget, gint response, gpointer user_data)
  839. {
  840. LLFilePicker* picker = (LLFilePicker*)user_data;
  841. lldebugs << "GTK DIALOG RESPONSE " << response << llendl;
  842. if (response == GTK_RESPONSE_ACCEPT)
  843. {
  844. GSList *file_list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(widget));
  845. g_slist_foreach(file_list, (GFunc)add_to_selectedfiles, user_data);
  846. g_slist_foreach(file_list, (GFunc)g_free, NULL);
  847. g_slist_free (file_list);
  848. }
  849. // set the default path for this usage context.
  850. picker->mContextToPathMap[picker->mCurContextName] =
  851. gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(widget));
  852. gtk_widget_destroy(widget);
  853. gtk_main_quit();
  854. }
  855. GtkWindow* LLFilePicker::buildFilePicker(bool is_save, bool is_folder, std::string context)
  856. {
  857. if (LLWindowSDL::ll_try_gtk_init())
  858. {
  859. GtkWidget *win = NULL;
  860. GtkFileChooserAction pickertype =
  861. is_save?
  862. (is_folder?
  863.  GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER :
  864.  GTK_FILE_CHOOSER_ACTION_SAVE) :
  865. (is_folder?
  866.  GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER :
  867.  GTK_FILE_CHOOSER_ACTION_OPEN);
  868. win = gtk_file_chooser_dialog_new(NULL, NULL,
  869.   pickertype,
  870.   GTK_STOCK_CANCEL,
  871.    GTK_RESPONSE_CANCEL,
  872.   is_folder ?
  873.   GTK_STOCK_APPLY :
  874.   (is_save ? 
  875.    GTK_STOCK_SAVE :
  876.    GTK_STOCK_OPEN),
  877.    GTK_RESPONSE_ACCEPT,
  878.   (gchar *)NULL);
  879. mCurContextName = context;
  880. // get the default path for this usage context if it's been
  881. // seen before.
  882. std::map<std::string,std::string>::iterator
  883. this_path = mContextToPathMap.find(context);
  884. if (this_path != mContextToPathMap.end())
  885. {
  886. gtk_file_chooser_set_current_folder
  887. (GTK_FILE_CHOOSER(win),
  888.  this_path->second.c_str());
  889. }
  890. #  if LL_X11
  891. // Make GTK tell the window manager to associate this
  892. // dialog with our non-GTK raw X11 window, which should try
  893. // to keep it on top etc.
  894. Window XWindowID = LLWindowSDL::get_SDL_XWindowID();
  895. if (None != XWindowID)
  896. {
  897. gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin
  898. GdkWindow *gdkwin = gdk_window_foreign_new(XWindowID);
  899. gdk_window_set_transient_for(GTK_WIDGET(win)->window,
  900.      gdkwin);
  901. }
  902. else
  903. {
  904. llwarns << "Hmm, couldn't get xwid to use for transient." << llendl;
  905. }
  906. #  endif //LL_X11
  907. g_signal_connect (GTK_FILE_CHOOSER(win),
  908.   "response",
  909.   G_CALLBACK(LLFilePicker::chooser_responder),
  910.   this);
  911. gtk_window_set_modal(GTK_WINDOW(win), TRUE);
  912. /* GTK 2.6: if (is_folder)
  913. gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(win),
  914. TRUE); */
  915. return GTK_WINDOW(win);
  916. }
  917. else
  918. {
  919. return NULL;
  920. }
  921. }
  922. static void add_common_filters_to_gtkchooser(GtkFileFilter *gfilter,
  923.      GtkWindow *picker,
  924.      std::string filtername)
  925. {
  926. gtk_file_filter_set_name(gfilter, filtername.c_str());
  927. gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(picker),
  928.     gfilter);
  929. GtkFileFilter *allfilter = gtk_file_filter_new();
  930. gtk_file_filter_add_pattern(allfilter, "*");
  931. gtk_file_filter_set_name(allfilter, LLTrans::getString("all_files").c_str());
  932. gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(picker), allfilter);
  933. gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(picker), gfilter);
  934. }
  935. static std::string add_simple_pattern_filter_to_gtkchooser(GtkWindow *picker,
  936.    std::string pattern,
  937.    std::string filtername)
  938. {
  939. GtkFileFilter *gfilter = gtk_file_filter_new();
  940. gtk_file_filter_add_pattern(gfilter, pattern.c_str());
  941. add_common_filters_to_gtkchooser(gfilter, picker, filtername);
  942. return filtername;
  943. }
  944. static std::string add_simple_mime_filter_to_gtkchooser(GtkWindow *picker,
  945. std::string mime,
  946. std::string filtername)
  947. {
  948. GtkFileFilter *gfilter = gtk_file_filter_new();
  949. gtk_file_filter_add_mime_type(gfilter, mime.c_str());
  950. add_common_filters_to_gtkchooser(gfilter, picker, filtername);
  951. return filtername;
  952. }
  953. static std::string add_wav_filter_to_gtkchooser(GtkWindow *picker)
  954. {
  955. return add_simple_mime_filter_to_gtkchooser(picker,  "audio/x-wav",
  956.     LLTrans::getString("sound_files") + " (*.wav)");
  957. }
  958. static std::string add_bvh_filter_to_gtkchooser(GtkWindow *picker)
  959. {
  960. return add_simple_pattern_filter_to_gtkchooser(picker,  "*.bvh",
  961.        LLTrans::getString("animation_files") + " (*.bvh)");
  962. }
  963. static std::string add_imageload_filter_to_gtkchooser(GtkWindow *picker)
  964. {
  965. GtkFileFilter *gfilter = gtk_file_filter_new();
  966. gtk_file_filter_add_pattern(gfilter, "*.tga");
  967. gtk_file_filter_add_mime_type(gfilter, "image/jpeg");
  968. gtk_file_filter_add_mime_type(gfilter, "image/png");
  969. gtk_file_filter_add_mime_type(gfilter, "image/bmp");
  970. std::string filtername = LLTrans::getString("image_files") + " (*.tga; *.bmp; *.jpg; *.png)";
  971. add_common_filters_to_gtkchooser(gfilter, picker, filtername);
  972. return filtername;
  973. }
  974. BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename )
  975. {
  976. BOOL rtn = FALSE;
  977. gViewerWindow->mWindow->beforeDialog();
  978. reset();
  979. GtkWindow* picker = buildFilePicker(true, false, "savefile");
  980. if (picker)
  981. {
  982. std::string suggest_name = "untitled";
  983. std::string suggest_ext = "";
  984. std::string caption = LLTrans::getString("save_file_verb") + " ";
  985. switch (filter)
  986. {
  987. case FFSAVE_WAV:
  988. caption += add_wav_filter_to_gtkchooser(picker);
  989. suggest_ext = ".wav";
  990. break;
  991. case FFSAVE_TGA:
  992. caption += add_simple_pattern_filter_to_gtkchooser
  993. (picker, "*.tga", LLTrans::getString("targa_image_files") + " (*.tga)");
  994. suggest_ext = ".tga";
  995. break;
  996. case FFSAVE_BMP:
  997. caption += add_simple_mime_filter_to_gtkchooser
  998. (picker, "image/bmp", LLTrans::getString("bitmap_image_files") + " (*.bmp)");
  999. suggest_ext = ".bmp";
  1000. break;
  1001. case FFSAVE_AVI:
  1002. caption += add_simple_mime_filter_to_gtkchooser
  1003. (picker, "video/x-msvideo",
  1004.  LLTrans::getString("avi_movie_file") + " (*.avi)");
  1005. suggest_ext = ".avi";
  1006. break;
  1007. case FFSAVE_ANIM:
  1008. caption += add_simple_pattern_filter_to_gtkchooser
  1009. (picker, "*.xaf", LLTrans::getString("xaf_animation_file") + " (*.xaf)");
  1010. suggest_ext = ".xaf";
  1011. break;
  1012. case FFSAVE_XML:
  1013. caption += add_simple_pattern_filter_to_gtkchooser
  1014. (picker, "*.xml", LLTrans::getString("xml_file") + " (*.xml)");
  1015. suggest_ext = ".xml";
  1016. break;
  1017. case FFSAVE_RAW:
  1018. caption += add_simple_pattern_filter_to_gtkchooser
  1019. (picker, "*.raw", LLTrans::getString("raw_file") + " (*.raw)");
  1020. suggest_ext = ".raw";
  1021. break;
  1022. case FFSAVE_J2C:
  1023. caption += add_simple_mime_filter_to_gtkchooser
  1024. (picker, "images/jp2",
  1025.  LLTrans::getString("compressed_image_files") + " (*.j2c)");
  1026. suggest_ext = ".j2c";
  1027. break;
  1028. default:;
  1029. break;
  1030. }
  1031. gtk_window_set_title(GTK_WINDOW(picker), caption.c_str());
  1032. if (filename.empty())
  1033. {
  1034. suggest_name += suggest_ext;
  1035. gtk_file_chooser_set_current_name
  1036. (GTK_FILE_CHOOSER(picker),
  1037.  suggest_name.c_str());
  1038. }
  1039. else
  1040. {
  1041. gtk_file_chooser_set_current_name
  1042. (GTK_FILE_CHOOSER(picker), filename.c_str());
  1043. }
  1044. gtk_widget_show_all(GTK_WIDGET(picker));
  1045. gtk_main();
  1046. rtn = (getFileCount() == 1);
  1047. }
  1048. gViewerWindow->mWindow->afterDialog();
  1049. return rtn;
  1050. }
  1051. BOOL LLFilePicker::getOpenFile( ELoadFilter filter )
  1052. {
  1053. BOOL rtn = FALSE;
  1054. gViewerWindow->mWindow->beforeDialog();
  1055. reset();
  1056. GtkWindow* picker = buildFilePicker(false, false, "openfile");
  1057. if (picker)
  1058. {
  1059. std::string caption = LLTrans::getString("load_file_verb") + " ";
  1060. std::string filtername = "";
  1061. switch (filter)
  1062. {
  1063. case FFLOAD_WAV:
  1064. filtername = add_wav_filter_to_gtkchooser(picker);
  1065. break;
  1066. case FFLOAD_ANIM:
  1067. filtername = add_bvh_filter_to_gtkchooser(picker);
  1068. break;
  1069. case FFLOAD_IMAGE:
  1070. filtername = add_imageload_filter_to_gtkchooser(picker);
  1071. break;
  1072. default:;
  1073. break;
  1074. }
  1075. caption += filtername;
  1076. gtk_window_set_title(GTK_WINDOW(picker), caption.c_str());
  1077. gtk_widget_show_all(GTK_WIDGET(picker));
  1078. gtk_main();
  1079. rtn = (getFileCount() == 1);
  1080. }
  1081. gViewerWindow->mWindow->afterDialog();
  1082. return rtn;
  1083. }
  1084. BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter )
  1085. {
  1086. BOOL rtn = FALSE;
  1087. gViewerWindow->mWindow->beforeDialog();
  1088. reset();
  1089. GtkWindow* picker = buildFilePicker(false, false, "openfile");
  1090. if (picker)
  1091. {
  1092. gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER(picker),
  1093.       TRUE);
  1094. gtk_window_set_title(GTK_WINDOW(picker), LLTrans::getString("load_files").c_str());
  1095. gtk_widget_show_all(GTK_WIDGET(picker));
  1096. gtk_main();
  1097. rtn = !mFiles.empty();
  1098. }
  1099. gViewerWindow->mWindow->afterDialog();
  1100. return rtn;
  1101. }
  1102. # else // LL_GTK
  1103. // Hacky stubs designed to facilitate fake getSaveFile and getOpenFile with
  1104. // static results, when we don't have a real filepicker.
  1105. BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename )
  1106. {
  1107. reset();
  1108. llinfos << "getSaveFile suggested filename is [" << filename
  1109. << "]" << llendl;
  1110. if (!filename.empty())
  1111. {
  1112. mFiles.push_back(gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter() + filename);
  1113. return TRUE;
  1114. }
  1115. return FALSE;
  1116. }
  1117. BOOL LLFilePicker::getOpenFile( ELoadFilter filter )
  1118. {
  1119. reset();
  1120. // HACK: Static filenames for 'open' until we implement filepicker
  1121. std::string filename = gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter() + "upload";
  1122. switch (filter)
  1123. {
  1124. case FFLOAD_WAV: filename += ".wav"; break;
  1125. case FFLOAD_IMAGE: filename += ".tga"; break;
  1126. case FFLOAD_ANIM: filename += ".bvh"; break;
  1127. default: break;
  1128. }
  1129. mFiles.push_back(filename);
  1130. llinfos << "getOpenFile: Will try to open file: " << hackyfilename << llendl;
  1131. return TRUE;
  1132. }
  1133. BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter )
  1134. {
  1135. reset();
  1136. return FALSE;
  1137. }
  1138. #endif // LL_GTK
  1139. #else // not implemented
  1140. BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename )
  1141. {
  1142. reset();
  1143. return FALSE;
  1144. }
  1145. BOOL LLFilePicker::getOpenFile( ELoadFilter filter )
  1146. {
  1147. reset();
  1148. return FALSE;
  1149. }
  1150. BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter )
  1151. {
  1152. reset();
  1153. return FALSE;
  1154. }
  1155. #endif