PageRenderTime 159ms CodeModel.GetById 11ms app.highlight 134ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/newview/llfilepicker.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1503 lines | 1420 code | 36 blank | 47 comment | 25 complexity | 842e598c8edd91b1a733252295ad36c4 MD5 | raw file
   1/** 
   2 * @file llfilepicker.cpp
   3 * @brief OS-specific file picker
   4 *
   5 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 * 
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 * 
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 * 
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 * 
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27#include "llviewerprecompiledheaders.h"
  28
  29#include "llfilepicker.h"
  30#include "llworld.h"
  31#include "llviewerwindow.h"
  32#include "llkeyboard.h"
  33#include "lldir.h"
  34#include "llframetimer.h"
  35#include "lltrans.h"
  36#include "llviewercontrol.h"
  37#include "llwindow.h"	// beforeDialog()
  38
  39#if LL_SDL
  40#include "llwindowsdl.h" // for some X/GTK utils to help with filepickers
  41#endif // LL_SDL
  42
  43//
  44// Globals
  45//
  46
  47LLFilePicker LLFilePicker::sInstance;
  48
  49#if LL_WINDOWS
  50#define SOUND_FILTER L"Sounds (*.wav)\0*.wav\0"
  51#define IMAGE_FILTER L"Images (*.tga; *.bmp; *.jpg; *.jpeg; *.png)\0*.tga;*.bmp;*.jpg;*.jpeg;*.png\0"
  52#define ANIM_FILTER L"Animations (*.bvh)\0*.bvh\0"
  53#define COLLADA_FILTER L"Scene (*.dae)\0*.dae\0"
  54#ifdef _CORY_TESTING
  55#define GEOMETRY_FILTER L"SL Geometry (*.slg)\0*.slg\0"
  56#endif
  57#define XML_FILTER L"XML files (*.xml)\0*.xml\0"
  58#define SLOBJECT_FILTER L"Objects (*.slobject)\0*.slobject\0"
  59#define RAW_FILTER L"RAW files (*.raw)\0*.raw\0"
  60#define MODEL_FILTER L"Model files (*.dae)\0*.dae\0"
  61#define SCRIPT_FILTER L"Script files (*.lsl)\0*.lsl\0"
  62#endif
  63
  64//
  65// Implementation
  66//
  67LLFilePicker::LLFilePicker()
  68	: mCurrentFile(0),
  69	  mLocked(false)
  70
  71{
  72	reset();
  73
  74#if LL_WINDOWS
  75	mOFN.lStructSize = sizeof(OPENFILENAMEW);
  76	mOFN.hwndOwner = NULL;  // Set later
  77	mOFN.hInstance = NULL;
  78	mOFN.lpstrCustomFilter = NULL;
  79	mOFN.nMaxCustFilter = 0;
  80	mOFN.lpstrFile = NULL;							// set in open and close
  81	mOFN.nMaxFile = LL_MAX_PATH;
  82	mOFN.lpstrFileTitle = NULL;
  83	mOFN.nMaxFileTitle = 0;
  84	mOFN.lpstrInitialDir = NULL;
  85	mOFN.lpstrTitle = NULL;
  86	mOFN.Flags = 0;									// set in open and close
  87	mOFN.nFileOffset = 0;
  88	mOFN.nFileExtension = 0;
  89	mOFN.lpstrDefExt = NULL;
  90	mOFN.lCustData = 0L;
  91	mOFN.lpfnHook = NULL;
  92	mOFN.lpTemplateName = NULL;
  93	mFilesW[0] = '\0';
  94#endif
  95
  96#if LL_DARWIN
  97	memset(&mNavOptions, 0, sizeof(mNavOptions));
  98	OSStatus	error = NavGetDefaultDialogCreationOptions(&mNavOptions);
  99	if (error == noErr)
 100	{
 101		mNavOptions.modality = kWindowModalityAppModal;
 102	}
 103#endif
 104}
 105
 106LLFilePicker::~LLFilePicker()
 107{
 108	// nothing
 109}
 110
 111// utility function to check if access to local file system via file browser 
 112// is enabled and if not, tidy up and indicate we're not allowed to do this.
 113bool LLFilePicker::check_local_file_access_enabled()
 114{
 115	// if local file browsing is turned off, return without opening dialog
 116	bool local_file_system_browsing_enabled = gSavedSettings.getBOOL("LocalFileSystemBrowsingEnabled");
 117	if ( ! local_file_system_browsing_enabled )
 118	{
 119		mFiles.clear();
 120		return false;
 121	}
 122
 123	return true;
 124}
 125
 126const std::string LLFilePicker::getFirstFile()
 127{
 128	mCurrentFile = 0;
 129	return getNextFile();
 130}
 131
 132const std::string LLFilePicker::getNextFile()
 133{
 134	if (mCurrentFile >= getFileCount())
 135	{
 136		mLocked = false;
 137		return std::string();
 138	}
 139	else
 140	{
 141		return mFiles[mCurrentFile++];
 142	}
 143}
 144
 145const std::string LLFilePicker::getCurFile()
 146{
 147	if (mCurrentFile >= getFileCount())
 148	{
 149		mLocked = false;
 150		return std::string();
 151	}
 152	else
 153	{
 154		return mFiles[mCurrentFile];
 155	}
 156}
 157
 158void LLFilePicker::reset()
 159{
 160	mLocked = false;
 161	mFiles.clear();
 162	mCurrentFile = 0;
 163}
 164
 165#if LL_WINDOWS
 166
 167BOOL LLFilePicker::setupFilter(ELoadFilter filter)
 168{
 169	BOOL res = TRUE;
 170	switch (filter)
 171	{
 172	case FFLOAD_ALL:
 173		mOFN.lpstrFilter = L"All Files (*.*)\0*.*\0" \
 174		SOUND_FILTER \
 175		IMAGE_FILTER \
 176		ANIM_FILTER \
 177		L"\0";
 178		break;
 179	case FFLOAD_WAV:
 180		mOFN.lpstrFilter = SOUND_FILTER \
 181			L"\0";
 182		break;
 183	case FFLOAD_IMAGE:
 184		mOFN.lpstrFilter = IMAGE_FILTER \
 185			L"\0";
 186		break;
 187	case FFLOAD_ANIM:
 188		mOFN.lpstrFilter = ANIM_FILTER \
 189			L"\0";
 190		break;
 191	case FFLOAD_COLLADA:
 192		mOFN.lpstrFilter = COLLADA_FILTER \
 193			L"\0";
 194		break;
 195#ifdef _CORY_TESTING
 196	case FFLOAD_GEOMETRY:
 197		mOFN.lpstrFilter = GEOMETRY_FILTER \
 198			L"\0";
 199		break;
 200#endif
 201	case FFLOAD_XML:
 202		mOFN.lpstrFilter = XML_FILTER \
 203			L"\0";
 204		break;
 205	case FFLOAD_SLOBJECT:
 206		mOFN.lpstrFilter = SLOBJECT_FILTER \
 207			L"\0";
 208		break;
 209	case FFLOAD_RAW:
 210		mOFN.lpstrFilter = RAW_FILTER \
 211			L"\0";
 212		break;
 213	case FFLOAD_MODEL:
 214		mOFN.lpstrFilter = MODEL_FILTER \
 215			L"\0";
 216		break;
 217	case FFLOAD_SCRIPT:
 218		mOFN.lpstrFilter = SCRIPT_FILTER \
 219			L"\0";
 220		break;
 221	default:
 222		res = FALSE;
 223		break;
 224	}
 225	return res;
 226}
 227
 228BOOL LLFilePicker::getOpenFile(ELoadFilter filter, bool blocking)
 229{
 230	if( mLocked )
 231	{
 232		return FALSE;
 233	}
 234	BOOL success = FALSE;
 235
 236	// if local file browsing is turned off, return without opening dialog
 237	if ( check_local_file_access_enabled() == false )
 238	{
 239		return FALSE;
 240	}
 241
 242	// don't provide default file selection
 243	mFilesW[0] = '\0';
 244
 245	mOFN.hwndOwner = (HWND)gViewerWindow->getPlatformWindow();
 246	mOFN.lpstrFile = mFilesW;
 247	mOFN.nMaxFile = SINGLE_FILENAME_BUFFER_SIZE;
 248	mOFN.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR ;
 249	mOFN.nFilterIndex = 1;
 250
 251	setupFilter(filter);
 252	
 253	if (blocking)
 254	{
 255		// Modal, so pause agent
 256		send_agent_pause();
 257	}
 258
 259	reset();
 260	
 261	// NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!!
 262	success = GetOpenFileName(&mOFN);
 263	if (success)
 264	{
 265		std::string filename = utf16str_to_utf8str(llutf16string(mFilesW));
 266		mFiles.push_back(filename);
 267	}
 268
 269	if (blocking)
 270	{
 271		send_agent_resume();
 272		// Account for the fact that the app has been stalled.
 273		LLFrameTimer::updateFrameTime();
 274	}
 275	
 276	return success;
 277}
 278
 279BOOL LLFilePicker::getMultipleOpenFiles(ELoadFilter filter)
 280{
 281	if( mLocked )
 282	{
 283		return FALSE;
 284	}
 285	BOOL success = FALSE;
 286
 287	// if local file browsing is turned off, return without opening dialog
 288	if ( check_local_file_access_enabled() == false )
 289	{
 290		return FALSE;
 291	}
 292
 293	// don't provide default file selection
 294	mFilesW[0] = '\0';
 295
 296	mOFN.hwndOwner = (HWND)gViewerWindow->getPlatformWindow();
 297	mOFN.lpstrFile = mFilesW;
 298	mOFN.nFilterIndex = 1;
 299	mOFN.nMaxFile = FILENAME_BUFFER_SIZE;
 300	mOFN.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR |
 301		OFN_EXPLORER | OFN_ALLOWMULTISELECT;
 302
 303	setupFilter(filter);
 304
 305	reset();
 306	
 307	// Modal, so pause agent
 308	send_agent_pause();
 309	// NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!!
 310	success = GetOpenFileName(&mOFN); // pauses until ok or cancel.
 311	if( success )
 312	{
 313		// The getopenfilename api doesn't tell us if we got more than
 314		// one file, so we have to test manually by checking string
 315		// lengths.
 316		if( wcslen(mOFN.lpstrFile) > mOFN.nFileOffset )	/*Flawfinder: ignore*/
 317		{
 318			std::string filename = utf16str_to_utf8str(llutf16string(mFilesW));
 319			mFiles.push_back(filename);
 320		}
 321		else
 322		{
 323			mLocked = true;
 324			WCHAR* tptrw = mFilesW;
 325			std::string dirname;
 326			while(1)
 327			{
 328				if (*tptrw == 0 && *(tptrw+1) == 0) // double '\0'
 329					break;
 330				if (*tptrw == 0)
 331					tptrw++; // shouldn't happen?
 332				std::string filename = utf16str_to_utf8str(llutf16string(tptrw));
 333				if (dirname.empty())
 334					dirname = filename + "\\";
 335				else
 336					mFiles.push_back(dirname + filename);
 337				tptrw += filename.size();
 338			}
 339		}
 340	}
 341	send_agent_resume();
 342
 343	// Account for the fact that the app has been stalled.
 344	LLFrameTimer::updateFrameTime();
 345	return success;
 346}
 347
 348BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename)
 349{
 350	if( mLocked )
 351	{
 352		return FALSE;
 353	}
 354	BOOL success = FALSE;
 355
 356	// if local file browsing is turned off, return without opening dialog
 357	if ( check_local_file_access_enabled() == false )
 358	{
 359		return FALSE;
 360	}
 361
 362	mOFN.lpstrFile = mFilesW;
 363	if (!filename.empty())
 364	{
 365		llutf16string tstring = utf8str_to_utf16str(filename);
 366		wcsncpy(mFilesW, tstring.c_str(), FILENAME_BUFFER_SIZE);	}	/*Flawfinder: ignore*/
 367	else
 368	{
 369		mFilesW[0] = '\0';
 370	}
 371	mOFN.hwndOwner = (HWND)gViewerWindow->getPlatformWindow();
 372
 373	switch( filter )
 374	{
 375	case FFSAVE_ALL:
 376		mOFN.lpstrDefExt = NULL;
 377		mOFN.lpstrFilter =
 378			L"All Files (*.*)\0*.*\0" \
 379			L"WAV Sounds (*.wav)\0*.wav\0" \
 380			L"Targa, Bitmap Images (*.tga; *.bmp)\0*.tga;*.bmp\0" \
 381			L"\0";
 382		break;
 383	case FFSAVE_WAV:
 384		if (filename.empty())
 385		{
 386			wcsncpy( mFilesW,L"untitled.wav", FILENAME_BUFFER_SIZE);	/*Flawfinder: ignore*/
 387		}
 388		mOFN.lpstrDefExt = L"wav";
 389		mOFN.lpstrFilter =
 390			L"WAV Sounds (*.wav)\0*.wav\0" \
 391			L"\0";
 392		break;
 393	case FFSAVE_TGA:
 394		if (filename.empty())
 395		{
 396			wcsncpy( mFilesW,L"untitled.tga", FILENAME_BUFFER_SIZE);	/*Flawfinder: ignore*/
 397		}
 398		mOFN.lpstrDefExt = L"tga";
 399		mOFN.lpstrFilter =
 400			L"Targa Images (*.tga)\0*.tga\0" \
 401			L"\0";
 402		break;
 403	case FFSAVE_BMP:
 404		if (filename.empty())
 405		{
 406			wcsncpy( mFilesW,L"untitled.bmp", FILENAME_BUFFER_SIZE);	/*Flawfinder: ignore*/
 407		}
 408		mOFN.lpstrDefExt = L"bmp";
 409		mOFN.lpstrFilter =
 410			L"Bitmap Images (*.bmp)\0*.bmp\0" \
 411			L"\0";
 412		break;
 413	case FFSAVE_PNG:
 414		if (filename.empty())
 415		{
 416			wcsncpy( mFilesW,L"untitled.png", FILENAME_BUFFER_SIZE);	/*Flawfinder: ignore*/
 417		}
 418		mOFN.lpstrDefExt = L"png";
 419		mOFN.lpstrFilter =
 420			L"PNG Images (*.png)\0*.png\0" \
 421			L"\0";
 422		break;
 423	case FFSAVE_JPEG:
 424		if (filename.empty())
 425		{
 426			wcsncpy( mFilesW,L"untitled.jpeg", FILENAME_BUFFER_SIZE);	/*Flawfinder: ignore*/
 427		}
 428		mOFN.lpstrDefExt = L"jpg";
 429		mOFN.lpstrFilter =
 430			L"JPEG Images (*.jpg *.jpeg)\0*.jpg;*.jpeg\0" \
 431			L"\0";
 432		break;
 433	case FFSAVE_AVI:
 434		if (filename.empty())
 435		{
 436			wcsncpy( mFilesW,L"untitled.avi", FILENAME_BUFFER_SIZE);	/*Flawfinder: ignore*/
 437		}
 438		mOFN.lpstrDefExt = L"avi";
 439		mOFN.lpstrFilter =
 440			L"AVI Movie File (*.avi)\0*.avi\0" \
 441			L"\0";
 442		break;
 443	case FFSAVE_ANIM:
 444		if (filename.empty())
 445		{
 446			wcsncpy( mFilesW,L"untitled.xaf", FILENAME_BUFFER_SIZE);	/*Flawfinder: ignore*/
 447		}
 448		mOFN.lpstrDefExt = L"xaf";
 449		mOFN.lpstrFilter =
 450			L"XAF Anim File (*.xaf)\0*.xaf\0" \
 451			L"\0";
 452		break;
 453#ifdef _CORY_TESTING
 454	case FFSAVE_GEOMETRY:
 455		if (filename.empty())
 456		{
 457			wcsncpy( mFilesW,L"untitled.slg", FILENAME_BUFFER_SIZE);	/*Flawfinder: ignore*/
 458		}
 459		mOFN.lpstrDefExt = L"slg";
 460		mOFN.lpstrFilter =
 461			L"SLG SL Geometry File (*.slg)\0*.slg\0" \
 462			L"\0";
 463		break;
 464#endif
 465	case FFSAVE_XML:
 466		if (filename.empty())
 467		{
 468			wcsncpy( mFilesW,L"untitled.xml", FILENAME_BUFFER_SIZE);	/*Flawfinder: ignore*/
 469		}
 470
 471		mOFN.lpstrDefExt = L"xml";
 472		mOFN.lpstrFilter =
 473			L"XML File (*.xml)\0*.xml\0" \
 474			L"\0";
 475		break;
 476	case FFSAVE_COLLADA:
 477		if (filename.empty())
 478		{
 479			wcsncpy( mFilesW,L"untitled.collada", FILENAME_BUFFER_SIZE);	/*Flawfinder: ignore*/
 480		}
 481		mOFN.lpstrDefExt = L"collada";
 482		mOFN.lpstrFilter =
 483			L"COLLADA File (*.collada)\0*.collada\0" \
 484			L"\0";
 485		break;
 486	case FFSAVE_RAW:
 487		if (filename.empty())
 488		{
 489			wcsncpy( mFilesW,L"untitled.raw", FILENAME_BUFFER_SIZE);	/*Flawfinder: ignore*/
 490		}
 491		mOFN.lpstrDefExt = L"raw";
 492		mOFN.lpstrFilter =	RAW_FILTER \
 493							L"\0";
 494		break;
 495	case FFSAVE_J2C:
 496		if (filename.empty())
 497		{
 498			wcsncpy( mFilesW,L"untitled.j2c", FILENAME_BUFFER_SIZE);
 499		}
 500		mOFN.lpstrDefExt = L"j2c";
 501		mOFN.lpstrFilter =
 502			L"Compressed Images (*.j2c)\0*.j2c\0" \
 503			L"\0";
 504		break;
 505	case FFSAVE_SCRIPT:
 506		if (filename.empty())
 507		{
 508			wcsncpy( mFilesW,L"untitled.lsl", FILENAME_BUFFER_SIZE);
 509		}
 510		mOFN.lpstrDefExt = L"txt";
 511		mOFN.lpstrFilter = L"LSL Files (*.lsl)\0*.lsl\0" L"\0";
 512		break;
 513	default:
 514		return FALSE;
 515	}
 516
 517 
 518	mOFN.nMaxFile = SINGLE_FILENAME_BUFFER_SIZE;
 519	mOFN.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST;
 520
 521	reset();
 522
 523	// Modal, so pause agent
 524	send_agent_pause();
 525	{
 526		// NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!!
 527		success = GetSaveFileName(&mOFN);
 528		if (success)
 529		{
 530			std::string filename = utf16str_to_utf8str(llutf16string(mFilesW));
 531			mFiles.push_back(filename);
 532		}
 533		gKeyboard->resetKeys();
 534	}
 535	send_agent_resume();
 536
 537	// Account for the fact that the app has been stalled.
 538	LLFrameTimer::updateFrameTime();
 539	return success;
 540}
 541
 542#elif LL_DARWIN
 543
 544Boolean LLFilePicker::navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode)
 545{
 546	Boolean		result = true;
 547	ELoadFilter filter = *((ELoadFilter*) callBackUD);
 548	OSStatus	error = noErr;
 549	
 550	if (filterMode == kNavFilteringBrowserList && filter != FFLOAD_ALL && (theItem->descriptorType == typeFSRef || theItem->descriptorType == typeFSS))
 551	{
 552		// navInfo is only valid for typeFSRef and typeFSS
 553		NavFileOrFolderInfo	*navInfo = (NavFileOrFolderInfo*) info;
 554		if (!navInfo->isFolder)
 555		{
 556			AEDesc	desc;
 557			error = AECoerceDesc(theItem, typeFSRef, &desc);
 558			if (error == noErr)
 559			{
 560				FSRef	fileRef;
 561				error = AEGetDescData(&desc, &fileRef, sizeof(fileRef));
 562				if (error == noErr)
 563				{
 564					LSItemInfoRecord	fileInfo;
 565					error = LSCopyItemInfoForRef(&fileRef, kLSRequestExtension | kLSRequestTypeCreator, &fileInfo);
 566					if (error == noErr)
 567					{
 568						if (filter == FFLOAD_IMAGE)
 569						{
 570							if (fileInfo.filetype != 'JPEG' && fileInfo.filetype != 'JPG ' && 
 571								fileInfo.filetype != 'BMP ' && fileInfo.filetype != 'TGA ' &&
 572								fileInfo.filetype != 'BMPf' && fileInfo.filetype != 'TPIC' &&
 573								fileInfo.filetype != 'PNG ' &&
 574								(fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("jpeg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
 575								CFStringCompare(fileInfo.extension, CFSTR("jpg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
 576								CFStringCompare(fileInfo.extension, CFSTR("bmp"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
 577								CFStringCompare(fileInfo.extension, CFSTR("tga"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
 578								CFStringCompare(fileInfo.extension, CFSTR("png"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
 579								)
 580							{
 581								result = false;
 582							}
 583						}
 584						else if (filter == FFLOAD_WAV)
 585						{
 586							if (fileInfo.filetype != 'WAVE' && fileInfo.filetype != 'WAV ' && 
 587								(fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("wave"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && 
 588								CFStringCompare(fileInfo.extension, CFSTR("wav"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
 589							)
 590							{
 591								result = false;
 592							}
 593						}
 594						else if (filter == FFLOAD_ANIM)
 595						{
 596							if (fileInfo.filetype != 'BVH ' && 
 597								(fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("bvh"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
 598							)
 599							{
 600								result = false;
 601							}
 602						}
 603						else if (filter == FFLOAD_COLLADA)
 604						{
 605							if (fileInfo.filetype != 'DAE ' && 
 606								(fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("dae"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
 607							)
 608							{
 609								result = false;
 610							}
 611						}
 612#ifdef _CORY_TESTING
 613						else if (filter == FFLOAD_GEOMETRY)
 614						{
 615							if (fileInfo.filetype != 'SLG ' && 
 616								(fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("slg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
 617							)
 618							{
 619								result = false;
 620							}
 621						}
 622#endif
 623						else if (filter == FFLOAD_SLOBJECT)
 624						{
 625							llwarns << "*** navOpenFilterProc: FFLOAD_SLOBJECT NOT IMPLEMENTED ***" << llendl;
 626						}
 627						else if (filter == FFLOAD_RAW)
 628						{
 629							if (fileInfo.filetype != '\?\?\?\?' && 
 630								(fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("raw"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
 631							)
 632							{
 633								result = false;
 634							}
 635						}
 636						else if (filter == FFLOAD_SCRIPT)
 637						{
 638							if (fileInfo.filetype != 'LSL ' &&
 639								(fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("lsl"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)) )
 640							{
 641								result = false;
 642							}
 643						}
 644						
 645						if (fileInfo.extension)
 646						{
 647							CFRelease(fileInfo.extension);
 648						}
 649					}
 650				}
 651				AEDisposeDesc(&desc);
 652			}
 653		}
 654	}
 655	return result;
 656}
 657
 658OSStatus	LLFilePicker::doNavChooseDialog(ELoadFilter filter)
 659{
 660	OSStatus		error = noErr;
 661	NavDialogRef	navRef = NULL;
 662	NavReplyRecord	navReply;
 663
 664	// if local file browsing is turned off, return without opening dialog
 665	if ( check_local_file_access_enabled() == false )
 666	{
 667		return FALSE;
 668	}
 669
 670	memset(&navReply, 0, sizeof(navReply));
 671	
 672	// NOTE: we are passing the address of a local variable here.  
 673	//   This is fine, because the object this call creates will exist for less than the lifetime of this function.
 674	//   (It is destroyed by NavDialogDispose() below.)
 675	error = NavCreateChooseFileDialog(&mNavOptions, NULL, NULL, NULL, navOpenFilterProc, (void*)(&filter), &navRef);
 676
 677	gViewerWindow->getWindow()->beforeDialog();
 678
 679	if (error == noErr)
 680		error = NavDialogRun(navRef);
 681
 682	gViewerWindow->getWindow()->afterDialog();
 683
 684	if (error == noErr)
 685		error = NavDialogGetReply(navRef, &navReply);
 686
 687	if (navRef)
 688		NavDialogDispose(navRef);
 689
 690	if (error == noErr && navReply.validRecord)
 691	{
 692		SInt32	count = 0;
 693		SInt32	index;
 694		
 695		// AE indexes are 1 based...
 696		error = AECountItems(&navReply.selection, &count);
 697		for (index = 1; index <= count; index++)
 698		{
 699			FSRef		fsRef;
 700			AEKeyword	theAEKeyword;
 701			DescType	typeCode;
 702			Size		actualSize = 0;
 703			char		path[MAX_PATH];	/*Flawfinder: ignore*/
 704			
 705			memset(&fsRef, 0, sizeof(fsRef));
 706			error = AEGetNthPtr(&navReply.selection, index, typeFSRef, &theAEKeyword, &typeCode, &fsRef, sizeof(fsRef), &actualSize);
 707			
 708			if (error == noErr)
 709				error = FSRefMakePath(&fsRef, (UInt8*) path, sizeof(path));
 710			
 711			if (error == noErr)
 712				mFiles.push_back(std::string(path));
 713		}
 714	}
 715	
 716	return error;
 717}
 718
 719OSStatus	LLFilePicker::doNavSaveDialog(ESaveFilter filter, const std::string& filename)
 720{
 721	OSStatus		error = noErr;
 722	NavDialogRef	navRef = NULL;
 723	NavReplyRecord	navReply;
 724	
 725	memset(&navReply, 0, sizeof(navReply));
 726	
 727	// Setup the type, creator, and extension
 728	OSType		type, creator;
 729	CFStringRef	extension = NULL;
 730	switch (filter)
 731	{
 732		case FFSAVE_WAV:
 733			type = 'WAVE';
 734			creator = 'TVOD';
 735			extension = CFSTR(".wav");
 736			break;
 737		
 738		case FFSAVE_TGA:
 739			type = 'TPIC';
 740			creator = 'prvw';
 741			extension = CFSTR(".tga");
 742			break;
 743		
 744		case FFSAVE_BMP:
 745			type = 'BMPf';
 746			creator = 'prvw';
 747			extension = CFSTR(".bmp");
 748			break;
 749		case FFSAVE_JPEG:
 750			type = 'JPEG';
 751			creator = 'prvw';
 752			extension = CFSTR(".jpeg");
 753			break;
 754		case FFSAVE_PNG:
 755			type = 'PNG ';
 756			creator = 'prvw';
 757			extension = CFSTR(".png");
 758			break;
 759		case FFSAVE_AVI:
 760			type = '\?\?\?\?';
 761			creator = '\?\?\?\?';
 762			extension = CFSTR(".mov");
 763			break;
 764
 765		case FFSAVE_ANIM:
 766			type = '\?\?\?\?';
 767			creator = '\?\?\?\?';
 768			extension = CFSTR(".xaf");
 769			break;
 770
 771#ifdef _CORY_TESTING
 772		case FFSAVE_GEOMETRY:
 773			type = '\?\?\?\?';
 774			creator = '\?\?\?\?';
 775			extension = CFSTR(".slg");
 776			break;
 777#endif		
 778		case FFSAVE_RAW:
 779			type = '\?\?\?\?';
 780			creator = '\?\?\?\?';
 781			extension = CFSTR(".raw");
 782			break;
 783
 784		case FFSAVE_J2C:
 785			type = '\?\?\?\?';
 786			creator = 'prvw';
 787			extension = CFSTR(".j2c");
 788			break;
 789		
 790		case FFSAVE_SCRIPT:
 791			type = 'LSL ';
 792			creator = '\?\?\?\?';
 793			extension = CFSTR(".lsl");
 794			break;
 795		
 796		case FFSAVE_ALL:
 797		default:
 798			type = '\?\?\?\?';
 799			creator = '\?\?\?\?';
 800			extension = CFSTR("");
 801			break;
 802	}
 803	
 804	// Create the dialog
 805	error = NavCreatePutFileDialog(&mNavOptions, type, creator, NULL, NULL, &navRef);
 806	if (error == noErr)
 807	{
 808		CFStringRef	nameString = NULL;
 809		bool		hasExtension = true;
 810		
 811		// Create a CFString of the initial file name
 812		if (!filename.empty())
 813			nameString = CFStringCreateWithCString(NULL, filename.c_str(), kCFStringEncodingUTF8);
 814		else
 815			nameString = CFSTR("Untitled");
 816			
 817		// Add the extension if one was not provided
 818		if (nameString && !CFStringHasSuffix(nameString, extension))
 819		{
 820			CFStringRef	tempString = nameString;
 821			hasExtension = false;
 822			nameString = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@"), tempString, extension);
 823			CFRelease(tempString);
 824		}
 825		
 826		// Set the name in the dialog
 827		if (nameString)
 828		{
 829			error = NavDialogSetSaveFileName(navRef, nameString);
 830			CFRelease(nameString);
 831		}
 832		else
 833		{
 834			error = paramErr;
 835		}
 836	}
 837	
 838	gViewerWindow->getWindow()->beforeDialog();
 839
 840	// Run the dialog
 841	if (error == noErr)
 842		error = NavDialogRun(navRef);
 843
 844	gViewerWindow->getWindow()->afterDialog();
 845
 846	if (error == noErr)
 847		error = NavDialogGetReply(navRef, &navReply);
 848	
 849	if (navRef)
 850		NavDialogDispose(navRef);
 851
 852	if (error == noErr && navReply.validRecord)
 853	{
 854		SInt32	count = 0;
 855		
 856		// AE indexes are 1 based...
 857		error = AECountItems(&navReply.selection, &count);
 858		if (count > 0)
 859		{
 860			// Get the FSRef to the containing folder
 861			FSRef		fsRef;
 862			AEKeyword	theAEKeyword;
 863			DescType	typeCode;
 864			Size		actualSize = 0;
 865			
 866			memset(&fsRef, 0, sizeof(fsRef));
 867			error = AEGetNthPtr(&navReply.selection, 1, typeFSRef, &theAEKeyword, &typeCode, &fsRef, sizeof(fsRef), &actualSize);
 868			
 869			if (error == noErr)
 870			{
 871				char	path[PATH_MAX];		/*Flawfinder: ignore*/
 872				char	newFileName[SINGLE_FILENAME_BUFFER_SIZE];	/*Flawfinder: ignore*/
 873				
 874				error = FSRefMakePath(&fsRef, (UInt8*)path, PATH_MAX);
 875				if (error == noErr)
 876				{
 877					if (CFStringGetCString(navReply.saveFileName, newFileName, sizeof(newFileName), kCFStringEncodingUTF8))
 878					{
 879						mFiles.push_back(std::string(path) + "/" +  std::string(newFileName));
 880					}
 881					else
 882					{
 883						error = paramErr;
 884					}
 885				}
 886				else
 887				{
 888					error = paramErr;
 889				}
 890			}
 891		}
 892	}
 893	
 894	return error;
 895}
 896
 897BOOL LLFilePicker::getOpenFile(ELoadFilter filter, bool blocking)
 898{
 899	if( mLocked )
 900		return FALSE;
 901
 902	BOOL success = FALSE;
 903
 904	// if local file browsing is turned off, return without opening dialog
 905	if ( check_local_file_access_enabled() == false )
 906	{
 907		return FALSE;
 908	}
 909
 910	OSStatus	error = noErr;
 911	
 912	reset();
 913	
 914	mNavOptions.optionFlags &= ~kNavAllowMultipleFiles;
 915
 916	if(filter == FFLOAD_ALL)	// allow application bundles etc. to be traversed; important for DEV-16869, but generally useful
 917	{
 918		// mNavOptions.optionFlags |= kNavAllowOpenPackages;
 919		mNavOptions.optionFlags |= kNavSupportPackages;
 920	}
 921	
 922	if (blocking)
 923	{
 924		// Modal, so pause agent
 925		send_agent_pause();
 926	}
 927
 928	{
 929		error = doNavChooseDialog(filter);
 930	}
 931	
 932	if (error == noErr)
 933	{
 934		if (getFileCount())
 935			success = true;
 936	}
 937
 938	if (blocking)
 939	{
 940		send_agent_resume();
 941		// Account for the fact that the app has been stalled.
 942		LLFrameTimer::updateFrameTime();
 943	}
 944
 945	return success;
 946}
 947
 948BOOL LLFilePicker::getMultipleOpenFiles(ELoadFilter filter)
 949{
 950	if( mLocked )
 951		return FALSE;
 952
 953	BOOL success = FALSE;
 954
 955	// if local file browsing is turned off, return without opening dialog
 956	if ( check_local_file_access_enabled() == false )
 957	{
 958		return FALSE;
 959	}
 960
 961	OSStatus	error = noErr;
 962
 963	reset();
 964	
 965	mNavOptions.optionFlags |= kNavAllowMultipleFiles;
 966	// Modal, so pause agent
 967	send_agent_pause();
 968	{
 969		error = doNavChooseDialog(filter);
 970	}
 971	send_agent_resume();
 972	if (error == noErr)
 973	{
 974		if (getFileCount())
 975			success = true;
 976		if (getFileCount() > 1)
 977			mLocked = true;
 978	}
 979
 980	// Account for the fact that the app has been stalled.
 981	LLFrameTimer::updateFrameTime();
 982	return success;
 983}
 984
 985BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename)
 986{
 987	if( mLocked )
 988		return FALSE;
 989	BOOL success = FALSE;
 990	OSStatus	error = noErr;
 991
 992	// if local file browsing is turned off, return without opening dialog
 993	if ( check_local_file_access_enabled() == false )
 994	{
 995		return FALSE;
 996	}
 997
 998	reset();
 999	
1000	mNavOptions.optionFlags &= ~kNavAllowMultipleFiles;
1001
1002	// Modal, so pause agent
1003	send_agent_pause();
1004	{
1005		error = doNavSaveDialog(filter, filename);
1006	}
1007	send_agent_resume();
1008	if (error == noErr)
1009	{
1010		if (getFileCount())
1011			success = true;
1012	}
1013
1014	// Account for the fact that the app has been stalled.
1015	LLFrameTimer::updateFrameTime();
1016	return success;
1017}
1018
1019#elif LL_LINUX || LL_SOLARIS
1020
1021# if LL_GTK
1022
1023// static
1024void LLFilePicker::add_to_selectedfiles(gpointer data, gpointer user_data)
1025{
1026	// We need to run g_filename_to_utf8 in the user's locale
1027	std::string saved_locale(setlocale(LC_ALL, NULL));
1028	setlocale(LC_ALL, "");
1029
1030	LLFilePicker* picker = (LLFilePicker*) user_data;
1031	GError *error = NULL;
1032	gchar* filename_utf8 = g_filename_to_utf8((gchar*)data,
1033						  -1, NULL, NULL, &error);
1034	if (error)
1035	{
1036		// *FIXME.
1037		// This condition should really be notified to the user, e.g.
1038		// through a message box.  Just logging it is inappropriate.
1039		
1040		// g_filename_display_name is ideal, but >= glib 2.6, so:
1041		// a hand-rolled hacky makeASCII which disallows control chars
1042		std::string display_name;
1043		for (const gchar *str = (const gchar *)data; *str; str++)
1044		{
1045			display_name += (char)((*str >= 0x20 && *str <= 0x7E) ? *str : '?');
1046		}
1047		llwarns << "g_filename_to_utf8 failed on \"" << display_name << "\": " << error->message << llendl;
1048	}
1049
1050	if (filename_utf8)
1051	{
1052		picker->mFiles.push_back(std::string(filename_utf8));
1053		lldebugs << "ADDED FILE " << filename_utf8 << llendl;
1054		g_free(filename_utf8);
1055	}
1056
1057	setlocale(LC_ALL, saved_locale.c_str());
1058}
1059
1060// static
1061void LLFilePicker::chooser_responder(GtkWidget *widget, gint response, gpointer user_data)
1062{
1063	LLFilePicker* picker = (LLFilePicker*)user_data;
1064
1065	lldebugs << "GTK DIALOG RESPONSE " << response << llendl;
1066
1067	if (response == GTK_RESPONSE_ACCEPT)
1068	{
1069		GSList *file_list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(widget));
1070		g_slist_foreach(file_list, (GFunc)add_to_selectedfiles, user_data);
1071		g_slist_foreach(file_list, (GFunc)g_free, NULL);
1072		g_slist_free (file_list);
1073	}
1074
1075	// set the default path for this usage context.
1076	picker->mContextToPathMap[picker->mCurContextName] =
1077		gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(widget));
1078
1079	gtk_widget_destroy(widget);
1080	gtk_main_quit();
1081}
1082
1083
1084GtkWindow* LLFilePicker::buildFilePicker(bool is_save, bool is_folder, std::string context)
1085{
1086	if (LLWindowSDL::ll_try_gtk_init())
1087	{
1088		GtkWidget *win = NULL;
1089		GtkFileChooserAction pickertype =
1090			is_save?
1091			(is_folder?
1092			 GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER :
1093			 GTK_FILE_CHOOSER_ACTION_SAVE) :
1094			(is_folder?
1095			 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER :
1096			 GTK_FILE_CHOOSER_ACTION_OPEN);
1097
1098		win = gtk_file_chooser_dialog_new(NULL, NULL,
1099						  pickertype,
1100						  GTK_STOCK_CANCEL,
1101						   GTK_RESPONSE_CANCEL,
1102						  is_folder ?
1103						  GTK_STOCK_APPLY :
1104						  (is_save ? 
1105						   GTK_STOCK_SAVE :
1106						   GTK_STOCK_OPEN),
1107						   GTK_RESPONSE_ACCEPT,
1108						  (gchar *)NULL);
1109		mCurContextName = context;
1110
1111		// get the default path for this usage context if it's been
1112		// seen before.
1113		std::map<std::string,std::string>::iterator
1114			this_path = mContextToPathMap.find(context);
1115		if (this_path != mContextToPathMap.end())
1116		{
1117			gtk_file_chooser_set_current_folder
1118				(GTK_FILE_CHOOSER(win),
1119				 this_path->second.c_str());
1120		}
1121
1122#  if LL_X11
1123		// Make GTK tell the window manager to associate this
1124		// dialog with our non-GTK raw X11 window, which should try
1125		// to keep it on top etc.
1126		Window XWindowID = LLWindowSDL::get_SDL_XWindowID();
1127		if (None != XWindowID)
1128		{
1129			gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin
1130			GdkWindow *gdkwin = gdk_window_foreign_new(XWindowID);
1131			gdk_window_set_transient_for(GTK_WIDGET(win)->window,
1132						     gdkwin);
1133		}
1134		else
1135		{
1136			llwarns << "Hmm, couldn't get xwid to use for transient." << llendl;
1137		}
1138#  endif //LL_X11
1139
1140		g_signal_connect (GTK_FILE_CHOOSER(win),
1141				  "response",
1142				  G_CALLBACK(LLFilePicker::chooser_responder),
1143				  this);
1144
1145		gtk_window_set_modal(GTK_WINDOW(win), TRUE);
1146
1147		/* GTK 2.6: if (is_folder)
1148			gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(win),
1149			TRUE); */
1150
1151		return GTK_WINDOW(win);
1152	}
1153	else
1154	{
1155		return NULL;
1156	}
1157}
1158
1159static void add_common_filters_to_gtkchooser(GtkFileFilter *gfilter,
1160					     GtkWindow *picker,
1161					     std::string filtername)
1162{	
1163	gtk_file_filter_set_name(gfilter, filtername.c_str());
1164	gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(picker),
1165				    gfilter);
1166	GtkFileFilter *allfilter = gtk_file_filter_new();
1167	gtk_file_filter_add_pattern(allfilter, "*");
1168	gtk_file_filter_set_name(allfilter, LLTrans::getString("all_files").c_str());
1169	gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(picker), allfilter);
1170	gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(picker), gfilter);
1171}
1172
1173static std::string add_simple_pattern_filter_to_gtkchooser(GtkWindow *picker,
1174							   std::string pattern,
1175							   std::string filtername)
1176{
1177	GtkFileFilter *gfilter = gtk_file_filter_new();
1178	gtk_file_filter_add_pattern(gfilter, pattern.c_str());
1179	add_common_filters_to_gtkchooser(gfilter, picker, filtername);
1180	return filtername;
1181}
1182
1183static std::string add_simple_mime_filter_to_gtkchooser(GtkWindow *picker,
1184							std::string mime,
1185							std::string filtername)
1186{
1187	GtkFileFilter *gfilter = gtk_file_filter_new();
1188	gtk_file_filter_add_mime_type(gfilter, mime.c_str());
1189	add_common_filters_to_gtkchooser(gfilter, picker, filtername);
1190	return filtername;
1191}
1192
1193static std::string add_wav_filter_to_gtkchooser(GtkWindow *picker)
1194{
1195	return add_simple_mime_filter_to_gtkchooser(picker,  "audio/x-wav",
1196						    LLTrans::getString("sound_files") + " (*.wav)");
1197}
1198
1199static std::string add_bvh_filter_to_gtkchooser(GtkWindow *picker)
1200{
1201	return add_simple_pattern_filter_to_gtkchooser(picker,  "*.bvh",
1202						       LLTrans::getString("animation_files") + " (*.bvh)");
1203}
1204
1205static std::string add_collada_filter_to_gtkchooser(GtkWindow *picker)
1206{
1207	return add_simple_pattern_filter_to_gtkchooser(picker,  "*.dae",
1208						       LLTrans::getString("scene_files") + " (*.dae)");
1209}
1210
1211static std::string add_imageload_filter_to_gtkchooser(GtkWindow *picker)
1212{
1213	GtkFileFilter *gfilter = gtk_file_filter_new();
1214	gtk_file_filter_add_pattern(gfilter, "*.tga");
1215	gtk_file_filter_add_mime_type(gfilter, "image/jpeg");
1216	gtk_file_filter_add_mime_type(gfilter, "image/png");
1217	gtk_file_filter_add_mime_type(gfilter, "image/bmp");
1218	std::string filtername = LLTrans::getString("image_files") + " (*.tga; *.bmp; *.jpg; *.png)";
1219	add_common_filters_to_gtkchooser(gfilter, picker, filtername);
1220	return filtername;
1221}
1222 
1223static std::string add_script_filter_to_gtkchooser(GtkWindow *picker)
1224{
1225	return add_simple_mime_filter_to_gtkchooser(picker,  "text/plain",
1226							LLTrans::getString("script_files") + " (*.lsl)");
1227}
1228
1229BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename )
1230{
1231	BOOL rtn = FALSE;
1232
1233	// if local file browsing is turned off, return without opening dialog
1234	if ( check_local_file_access_enabled() == false )
1235	{
1236		return FALSE;
1237	}
1238
1239	gViewerWindow->getWindow()->beforeDialog();
1240
1241	reset();
1242	
1243	GtkWindow* picker = buildFilePicker(true, false, "savefile");
1244
1245	if (picker)
1246	{
1247		std::string suggest_name = "untitled";
1248		std::string suggest_ext = "";
1249		std::string caption = LLTrans::getString("save_file_verb") + " ";
1250		switch (filter)
1251		{
1252		case FFSAVE_WAV:
1253			caption += add_wav_filter_to_gtkchooser(picker);
1254			suggest_ext = ".wav";
1255			break;
1256		case FFSAVE_TGA:
1257			caption += add_simple_pattern_filter_to_gtkchooser
1258				(picker, "*.tga", LLTrans::getString("targa_image_files") + " (*.tga)");
1259			suggest_ext = ".tga";
1260			break;
1261		case FFSAVE_BMP:
1262			caption += add_simple_mime_filter_to_gtkchooser
1263				(picker, "image/bmp", LLTrans::getString("bitmap_image_files") + " (*.bmp)");
1264			suggest_ext = ".bmp";
1265			break;
1266		case FFSAVE_AVI:
1267			caption += add_simple_mime_filter_to_gtkchooser
1268				(picker, "video/x-msvideo",
1269				 LLTrans::getString("avi_movie_file") + " (*.avi)");
1270			suggest_ext = ".avi";
1271			break;
1272		case FFSAVE_ANIM:
1273			caption += add_simple_pattern_filter_to_gtkchooser
1274				(picker, "*.xaf", LLTrans::getString("xaf_animation_file") + " (*.xaf)");
1275			suggest_ext = ".xaf";
1276			break;
1277		case FFSAVE_XML:
1278			caption += add_simple_pattern_filter_to_gtkchooser
1279				(picker, "*.xml", LLTrans::getString("xml_file") + " (*.xml)");
1280			suggest_ext = ".xml";
1281			break;
1282		case FFSAVE_RAW:
1283			caption += add_simple_pattern_filter_to_gtkchooser
1284				(picker, "*.raw", LLTrans::getString("raw_file") + " (*.raw)");
1285			suggest_ext = ".raw";
1286			break;
1287		case FFSAVE_J2C:
1288			caption += add_simple_mime_filter_to_gtkchooser
1289				(picker, "images/jp2",
1290				 LLTrans::getString("compressed_image_files") + " (*.j2c)");
1291			suggest_ext = ".j2c";
1292			break;
1293		case FFSAVE_SCRIPT:
1294			caption += add_script_filter_to_gtkchooser(picker);
1295			suggest_ext = ".lsl";
1296			break;
1297		default:;
1298			break;
1299		}
1300		
1301		gtk_window_set_title(GTK_WINDOW(picker), caption.c_str());
1302
1303		if (filename.empty())
1304		{
1305			suggest_name += suggest_ext;
1306
1307			gtk_file_chooser_set_current_name
1308				(GTK_FILE_CHOOSER(picker),
1309				 suggest_name.c_str());
1310		}
1311		else
1312		{
1313			gtk_file_chooser_set_current_name
1314				(GTK_FILE_CHOOSER(picker), filename.c_str());
1315		}
1316
1317		gtk_widget_show_all(GTK_WIDGET(picker));
1318		gtk_main();
1319
1320		rtn = (getFileCount() == 1);
1321	}
1322
1323	gViewerWindow->getWindow()->afterDialog();
1324
1325	return rtn;
1326}
1327
1328BOOL LLFilePicker::getOpenFile( ELoadFilter filter, bool blocking )
1329{
1330	BOOL rtn = FALSE;
1331
1332	// if local file browsing is turned off, return without opening dialog
1333	if ( check_local_file_access_enabled() == false )
1334	{
1335		return FALSE;
1336	}
1337
1338	gViewerWindow->getWindow()->beforeDialog();
1339
1340	reset();
1341	
1342	GtkWindow* picker = buildFilePicker(false, false, "openfile");
1343
1344	if (picker)
1345	{
1346		std::string caption = LLTrans::getString("load_file_verb") + " ";
1347		std::string filtername = "";
1348		switch (filter)
1349		{
1350		case FFLOAD_WAV:
1351			filtername = add_wav_filter_to_gtkchooser(picker);
1352			break;
1353		case FFLOAD_ANIM:
1354			filtername = add_bvh_filter_to_gtkchooser(picker);
1355			break;
1356		case FFLOAD_COLLADA:
1357			filtername = add_collada_filter_to_gtkchooser(picker);
1358			break;
1359		case FFLOAD_IMAGE:
1360			filtername = add_imageload_filter_to_gtkchooser(picker);
1361			break;
1362		case FFLOAD_SCRIPT:
1363			filtername = add_script_filter_to_gtkchooser(picker);
1364			break;
1365		default:;
1366			break;
1367		}
1368
1369		caption += filtername;
1370		
1371		gtk_window_set_title(GTK_WINDOW(picker), caption.c_str());
1372
1373		gtk_widget_show_all(GTK_WIDGET(picker));
1374		gtk_main();
1375
1376		rtn = (getFileCount() == 1);
1377	}
1378
1379	gViewerWindow->getWindow()->afterDialog();
1380
1381	return rtn;
1382}
1383
1384BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter )
1385{
1386	BOOL rtn = FALSE;
1387
1388	// if local file browsing is turned off, return without opening dialog
1389	if ( check_local_file_access_enabled() == false )
1390	{
1391		return FALSE;
1392	}
1393
1394	gViewerWindow->getWindow()->beforeDialog();
1395
1396	reset();
1397	
1398	GtkWindow* picker = buildFilePicker(false, false, "openfile");
1399
1400	if (picker)
1401	{
1402		gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER(picker),
1403						      TRUE);
1404
1405		gtk_window_set_title(GTK_WINDOW(picker), LLTrans::getString("load_files").c_str());
1406
1407		gtk_widget_show_all(GTK_WIDGET(picker));
1408		gtk_main();
1409		rtn = !mFiles.empty();
1410	}
1411
1412	gViewerWindow->getWindow()->afterDialog();
1413
1414	return rtn;
1415}
1416
1417# else // LL_GTK
1418
1419// Hacky stubs designed to facilitate fake getSaveFile and getOpenFile with
1420// static results, when we don't have a real filepicker.
1421
1422BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename )
1423{
1424	// if local file browsing is turned off, return without opening dialog
1425	// (Even though this is a stub, I think we still should not return anything at all)
1426	if ( check_local_file_access_enabled() == false )
1427	{
1428		return FALSE;
1429	}
1430
1431	reset();
1432	
1433	llinfos << "getSaveFile suggested filename is [" << filename
1434		<< "]" << llendl;
1435	if (!filename.empty())
1436	{
1437		mFiles.push_back(gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter() + filename);
1438		return TRUE;
1439	}
1440	return FALSE;
1441}
1442
1443BOOL LLFilePicker::getOpenFile( ELoadFilter filter )
1444{
1445	// if local file browsing is turned off, return without opening dialog
1446	// (Even though this is a stub, I think we still should not return anything at all)
1447	if ( check_local_file_access_enabled() == false )
1448	{
1449		return FALSE;
1450	}
1451
1452	reset();
1453	
1454	// HACK: Static filenames for 'open' until we implement filepicker
1455	std::string filename = gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter() + "upload";
1456	switch (filter)
1457	{
1458	case FFLOAD_WAV: filename += ".wav"; break;
1459	case FFLOAD_IMAGE: filename += ".tga"; break;
1460	case FFLOAD_ANIM: filename += ".bvh"; break;
1461	default: break;
1462	}
1463	mFiles.push_back(filename);
1464	llinfos << "getOpenFile: Will try to open file: " << hackyfilename << llendl;
1465	return TRUE;
1466}
1467
1468BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter )
1469{
1470	// if local file browsing is turned off, return without opening dialog
1471	// (Even though this is a stub, I think we still should not return anything at all)
1472	if ( check_local_file_access_enabled() == false )
1473	{
1474		return FALSE;
1475	}
1476
1477	reset();
1478	return FALSE;
1479}
1480
1481#endif // LL_GTK
1482
1483#else // not implemented
1484
1485BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename )
1486{
1487	reset();	
1488	return FALSE;
1489}
1490
1491BOOL LLFilePicker::getOpenFile( ELoadFilter filter )
1492{
1493	reset();
1494	return FALSE;
1495}
1496
1497BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter )
1498{
1499	reset();
1500	return FALSE;
1501}
1502
1503#endif