fileDialog.cpp
Engine/source/platform/nativeDialogs/fileDialog.cpp
Detailed Description
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "console/simBase.h" 25#include "platform/nativeDialogs/fileDialog.h" 26#include "platform/threads/mutex.h" 27#include "core/util/safeDelete.h" 28#include "math/mMath.h" 29#include "core/strings/unicode.h" 30#include "console/consoleTypes.h" 31#include "platform/profiler.h" 32#include "console/engineAPI.h" 33#include <nfd.h> 34#include "core/strings/stringUnit.h" 35#include "core/frameAllocator.h" 36 37#if defined(TORQUE_SDL) 38//----------------------------------------------------------------------------- 39// PlatformFileDlgData Implementation 40//----------------------------------------------------------------------------- 41FileDialogData::FileDialogData() 42{ 43 // Default Path 44 // 45 // Try to provide consistent experience by recalling the last file path 46 // - else 47 // Default to Working Directory if last path is not set or is invalid 48 mDefaultPath = StringTable->insert(Con::getVariable("Tools::FileDialogs::LastFilePath")); 49 if (mDefaultPath == StringTable->lookup("") || !Platform::isDirectory(mDefaultPath)) 50 mDefaultPath = Platform::getCurrentDirectory(); 51 52 mDefaultFile = StringTable->EmptyString(); 53 mFilters = StringTable->EmptyString(); 54 mFile = StringTable->EmptyString(); 55 mTitle = StringTable->EmptyString(); 56 57 mStyle = 0; 58 mOpaqueData = NULL; 59 60} 61FileDialogData::~FileDialogData() 62{ 63 64} 65 66//----------------------------------------------------------------------------- 67// FileDialog Implementation 68//----------------------------------------------------------------------------- 69IMPLEMENT_CONOBJECT(FileDialog); 70 71ConsoleDocClass(FileDialog, 72 "@brief Base class responsible for displaying an OS file browser.\n\n" 73 74 "FileDialog is a platform agnostic dialog interface for querying the user for " 75 "file locations. It is designed to be used through the exposed scripting interface.\n\n" 76 77 "FileDialog is the base class for Native File Dialog controls in Torque. It provides these basic areas of functionality:\n\n" 78 " - Inherits from SimObject and is exposed to the scripting interface\n" 79 " - Provides blocking interface to allow instant return to script execution\n" 80 " - Simple object configuration makes practical use easy and effective\n\n" 81 82 "FileDialog is *NOT* intended to be used directly in script and is only exposed to script to expose generic file dialog attributes.\n\n" 83 84 "This base class is usable in TorqueScript, but is does not specify what functionality is intended (open or save?). " 85 "Its children, OpenFileDialog and SaveFileDialog, do make use of DialogStyle flags and do make use of specific funcationality. " 86 "These are the preferred classes to use\n\n" 87 88 "However, the FileDialog base class does contain the key properties and important method for file browing. The most " 89 "important function is Execute(). This is used by both SaveFileDialog and OpenFileDialog to initiate the browser.\n\n" 90 91 "@tsexample\n" 92 "// NOTE: This is not he preferred class to use, but this still works\n\n" 93 "// Create the file dialog\n" 94 "%baseFileDialog = new FileDialog()\n" 95 "{\n" 96 " // Allow browsing of all file types\n" 97 " filters = \"*.*\";\n\n" 98 " // No default file\n" 99 " defaultFile = "";\n\n" 100 " // Set default path relative to project\n" 101 " defaultPath = \"./\";\n\n" 102 " // Set the title\n" 103 " title = \"Durpa\";\n\n" 104 " // Allow changing of path you are browsing\n" 105 " changePath = true;\n" 106 "};\n\n" 107 " // Launch the file dialog\n" 108 " %baseFileDialog.Execute();\n" 109 " \n" 110 " // Don't forget to cleanup\n" 111 " %baseFileDialog.delete();\n\n\n" 112 "@endtsexample\n\n" 113 114 "@note FileDialog and its related classes are only availble in a Tools build of Torque.\n\n" 115 116 "@see OpenFileDialog for a practical example on opening a file\n" 117 "@see SaveFileDialog for a practical example of saving a file\n" 118 119 "@ingroup FileSystem\n" 120 ); 121 122FileDialog::FileDialog() : mData() 123{ 124 // Default to File Must Exist Open Dialog style 125 mData.mStyle = FileDialogData::FDS_OPEN | FileDialogData::FDS_MUSTEXIST; 126 mChangePath = false; 127 mForceRelativePath = true; 128 mBoolTranslator = false; 129} 130 131FileDialog::~FileDialog() 132{ 133} 134 135void FileDialog::initPersistFields() 136{ 137 addProtectedField("defaultPath", TypeString, Offset(mData.mDefaultPath, FileDialog), &setDefaultPath, &defaultProtectedGetFn, 138 "The default directory path when the dialog is shown."); 139 140 addProtectedField("defaultFile", TypeString, Offset(mData.mDefaultFile, FileDialog), &setDefaultFile, &defaultProtectedGetFn, 141 "The default file path when the dialog is shown."); 142 143 addProtectedField("fileName", TypeString, Offset(mData.mFile, FileDialog), &setFile, &defaultProtectedGetFn, 144 "The default file name when the dialog is shown."); 145 146 addProtectedField("filters", TypeString, Offset(mData.mFilters, FileDialog), &setFilters, &defaultProtectedGetFn, 147 "The filter string for limiting the types of files visible in the dialog. It makes use of the pipe symbol '|' " 148 "as a delimiter. For example:\n\n" 149 "'All Files|*.*'\n\n" 150 "'Image Files|*.png;*.jpg|Png Files|*.png|Jepg Files|*.jpg'"); 151 152 addField("title", TypeString, Offset(mData.mTitle, FileDialog), 153 "The title for the dialog."); 154 155 addProtectedField("changePath", TypeBool, Offset(mChangePath, FileDialog), &setChangePath, &getChangePath, 156 "True/False whether to set the working directory to the directory returned by the dialog."); 157 158 addField("forceRelativePath", TypeBool, Offset(mForceRelativePath, FileDialog), "True/False whether to the path returned is always made to be relative."); 159 160 Parent::initPersistFields(); 161} 162 163static const U32 convertUTF16toUTF8DoubleNULL(const UTF16 *unistring, UTF8 *outbuffer, U32 len) 164{ 165 AssertFatal(len >= 1, "Buffer for unicode conversion must be large enough to hold at least the null terminator."); 166 PROFILE_START(convertUTF16toUTF8DoubleNULL); 167 U32 walked, nCodeunits, codeunitLen; 168 UTF32 middleman; 169 170 nCodeunits = 0; 171 while (!(*unistring == '\0' && *(unistring + 1) == '\0') && nCodeunits + 3 < len) 172 { 173 walked = 1; 174 middleman = oneUTF16toUTF32(unistring, &walked); 175 codeunitLen = oneUTF32toUTF8(middleman, &outbuffer[nCodeunits]); 176 unistring += walked; 177 nCodeunits += codeunitLen; 178 } 179 180 nCodeunits = getMin(nCodeunits, len - 1); 181 outbuffer[nCodeunits] = '\0'; 182 outbuffer[nCodeunits + 1] = '\0'; 183 184 PROFILE_END(); 185 return nCodeunits; 186} 187 188// 189// Execute Method 190// 191bool FileDialog::Execute() 192{ 193 String strippedFilters; 194 195 U32 filtersCount = StringUnit::getUnitCount(mData.mFilters, "|"); 196 197 for (U32 i = 1; i < filtersCount; ++i) 198 { 199 //The first of each pair is the name, which we'll skip because NFD doesn't support named filters atm 200 String filter = StringUnit::getUnit(mData.mFilters, i, "|"); 201 202 if (!String::compare(filter.c_str(), "*.*")) 203 continue; 204 205 U32 subFilterCount = StringUnit::getUnitCount(filter, ";"); 206 207 //if we have a 'super filter', break it down to sub-options as well 208 if (subFilterCount > 1) 209 { 210 String suffixFilter; 211 String subFilters; 212 213 for (U32 f = 0; f < subFilterCount; ++f) 214 { 215 String subFilter = StringUnit::getUnit(filter, f, ";"); 216 217 suffixFilter += String::ToLower(subFilter) + "," + String::ToUpper(subFilter) + ","; 218 subFilters += String::ToLower(subFilter) + "," + String::ToUpper(subFilter) + ";"; 219 } 220 221 suffixFilter = suffixFilter.substr(0, suffixFilter.length() - 1); 222 suffixFilter += ";"; 223 224 strippedFilters += suffixFilter + subFilters; 225 } 226 else //otherwise, just add the filter 227 { 228 strippedFilters += String::ToLower(filter) + "," + String::ToUpper(filter) + ";"; 229 } 230 231 ++i; 232 if (i < filtersCount - 2) 233 strippedFilters += String(";"); 234 } 235 236 //strip the last character, if it's unneeded 237 if (strippedFilters.endsWith(";")) 238 { 239 strippedFilters = strippedFilters.substr(0, strippedFilters.length() - 1); 240 } 241 242 strippedFilters.replace("*.", ""); 243 244 // Get the current working directory, so we can back up to it once Windows has 245 // done its craziness and messed with it. 246 StringTableEntry cwd = Platform::getCurrentDirectory(); 247 if (mData.mDefaultPath == StringTable->lookup("") || !Platform::isDirectory(mData.mDefaultPath)) 248 mData.mDefaultPath = cwd; 249 String rootDir = String(cwd); 250 // Execute Dialog (Blocking Call) 251 nfdchar_t *outPath = NULL; 252 nfdpathset_t pathSet; 253 254 nfdresult_t result = NFD_ERROR; 255 String defaultPath = String(mData.mDefaultPath); 256#if defined(TORQUE_OS_WIN) 257 defaultPath.replace("/", "\\"); 258 rootDir.replace("/", "\\"); 259#endif 260 261 if (mData.mStyle & FileDialogData::FDS_OPEN && !(mData.mStyle & FileDialogData::FDS_BROWSEFOLDER)) 262 result = NFD_OpenDialog(strippedFilters.c_str(), defaultPath.c_str(), &outPath); 263 else if (mData.mStyle & FileDialogData::FDS_SAVE && !(mData.mStyle & FileDialogData::FDS_BROWSEFOLDER)) 264 result = NFD_SaveDialog(strippedFilters.c_str(), defaultPath.c_str(), &outPath); 265 else if (mData.mStyle & FileDialogData::FDS_MULTIPLEFILES) 266 result = NFD_OpenDialogMultiple(strippedFilters.c_str(), defaultPath.c_str(), &pathSet); 267 else if (mData.mStyle & FileDialogData::FDS_BROWSEFOLDER) 268 result = NFD_PickFolder(defaultPath.c_str(), &outPath); 269 270 if (result == NFD_CANCEL) 271 { 272 return false; 273 } 274 275 String resultPath = String(outPath).replace(rootDir, String("")); 276 if(resultPath[0] == '\\') 277 resultPath = resultPath.replace(0, 1, String("")).c_str(); //kill '\\' prefix 278 resultPath = resultPath.replace(String("\\"), String("/")); 279 280 // Did we select a file? 281 if (result != NFD_OKAY) 282 { 283 Con::errorf("NFD plugin error: %s", NFD_GetError()); 284 return false; 285 } 286 // Store the result on our object 287 if (mData.mStyle & FileDialogData::FDS_OPEN || mData.mStyle & FileDialogData::FDS_SAVE) 288 { 289 // Single file selection, do it the easy way 290 if(mForceRelativePath) 291 mData.mFile = Con::getReturnBuffer(Platform::makeRelativePathName(resultPath.c_str(), NULL)); 292 else 293 mData.mFile = Con::getReturnBuffer(resultPath.c_str()); 294 } 295 else if (mData.mStyle & FileDialogData::FDS_MULTIPLEFILES) 296 { 297 //check if we have multiple files actually selected or not 298 U32 fileCount = NFD_PathSet_GetCount(&pathSet); 299 if (fileCount > 1) 300 { 301 //yep, so parse through them and prep our return 302 for (U32 i = 0; i < fileCount; ++i) 303 { 304 nfdchar_t *path = NFD_PathSet_GetPath(&pathSet, i); 305 setDataField(StringTable->insert("files"), Con::getIntArg(i), path); 306 } 307 308 setDataField(StringTable->insert("fileCount"), NULL, Con::getIntArg(fileCount)); 309 } 310 else 311 { 312 //nope, just one file, so set it as normal 313 if (mForceRelativePath) 314 setDataField(StringTable->insert("files"), "0", Platform::makeRelativePathName(resultPath.c_str(), NULL)); 315 else 316 setDataField(StringTable->insert("files"), "0", resultPath.c_str()); 317 318 setDataField(StringTable->insert("fileCount"), NULL, "1"); 319 } 320 } 321 322 // Return success. 323 return true; 324} 325 326DefineEngineMethod(FileDialog, Execute, bool, (), , 327 "@brief Launches the OS file browser\n\n" 328 329 "After an Execute() call, the chosen file name and path is available in one of two areas. " 330 "If only a single file selection is permitted, the results will be stored in the @a fileName " 331 "attribute.\n\n" 332 333 "If multiple file selection is permitted, the results will be stored in the " 334 "@a files array. The total number of files in the array will be stored in the " 335 "@a fileCount attribute.\n\n" 336 337 "@tsexample\n" 338 "// NOTE: This is not he preferred class to use, but this still works\n\n" 339 "// Create the file dialog\n" 340 "%baseFileDialog = new FileDialog()\n" 341 "{\n" 342 " // Allow browsing of all file types\n" 343 " filters = \"*.*\";\n\n" 344 " // No default file\n" 345 " defaultFile = "";\n\n" 346 " // Set default path relative to project\n" 347 " defaultPath = \"./\";\n\n" 348 " // Set the title\n" 349 " title = \"Durpa\";\n\n" 350 " // Allow changing of path you are browsing\n" 351 " changePath = true;\n" 352 "};\n\n" 353 " // Launch the file dialog\n" 354 " %baseFileDialog.Execute();\n" 355 " \n" 356 " // Don't forget to cleanup\n" 357 " %baseFileDialog.delete();\n\n\n" 358 359 " // A better alternative is to use the \n" 360 " // derived classes which are specific to file open and save\n\n" 361 " // Create a dialog dedicated to opening files\n" 362 " %openFileDlg = new OpenFileDialog()\n" 363 " {\n" 364 " // Look for jpg image files\n" 365 " // First part is the descriptor|second part is the extension\n" 366 " Filters = \"Jepg Files|*.jpg\";\n" 367 " // Allow browsing through other folders\n" 368 " ChangePath = true;\n\n" 369 " // Only allow opening of one file at a time\n" 370 " MultipleFiles = false;\n" 371 " };\n\n" 372 " // Launch the open file dialog\n" 373 " %result = %openFileDlg.Execute();\n\n" 374 " // Obtain the chosen file name and path\n" 375 " if ( %result )\n" 376 " {\n" 377 " %seletedFile = %openFileDlg.file;\n" 378 " }\n" 379 " else\n" 380 " {\n" 381 " %selectedFile = \"\";\n" 382 " }\n" 383 " // Cleanup\n" 384 " %openFileDlg.delete();\n\n\n" 385 386 " // Create a dialog dedicated to saving a file\n" 387 " %saveFileDlg = new SaveFileDialog()\n" 388 " {\n" 389 " // Only allow for saving of COLLADA files\n" 390 " Filters = \"COLLADA Files (*.dae)|*.dae|\";\n\n" 391 " // Default save path to where the WorldEditor last saved\n" 392 " DefaultPath = $pref::WorldEditor::LastPath;\n\n" 393 " // No default file specified\n" 394 " DefaultFile = \"\";\n\n" 395 " // Do not allow the user to change to a new directory\n" 396 " ChangePath = false;\n\n" 397 " // Prompt the user if they are going to overwrite an existing file\n" 398 " OverwritePrompt = true;\n" 399 " };\n\n" 400 " // Launch the save file dialog\n" 401 " %result = %saveFileDlg.Execute();\n\n" 402 " // Obtain the file name\n" 403 " %selectedFile = \"\";\n" 404 " if ( %result )\n" 405 " %selectedFile = %saveFileDlg.file;\n\n" 406 " // Cleanup\n" 407 " %saveFileDlg.delete();\n" 408 "@endtsexample\n\n" 409 410 "@return True if the file was selected was successfully found (opened) or declared (saved).") 411{ 412 return object->Execute(); 413} 414 415//----------------------------------------------------------------------------- 416// Dialog Filters 417//----------------------------------------------------------------------------- 418bool FileDialog::setFilters(void *object, const char *index, const char *data) 419{ 420 // Will do validate on write at some point. 421 if (!data) 422 return true; 423 424 return true; 425 426}; 427 428 429//----------------------------------------------------------------------------- 430// Default Path Property - String Validated on Write 431//----------------------------------------------------------------------------- 432bool FileDialog::setDefaultPath(void *object, const char *index, const char *data) 433{ 434 if (!data || !dStrncmp(data, "", 1)) 435 return true; 436 437 // Copy and Backslash the path (Windows dialogs are VERY picky about this format) 438 static char szPathValidate[512]; 439 dStrcpy(szPathValidate, data, 512); 440 441 Platform::makeFullPathName(data, szPathValidate, sizeof(szPathValidate)); 442 //backslash( szPathValidate ); 443 444 // Remove any trailing \'s 445 S8 validateLen = dStrlen(szPathValidate); 446 if (szPathValidate[validateLen - 1] == '\\') 447 szPathValidate[validateLen - 1] = '\0'; 448 449 // Now check 450 if (Platform::isDirectory(szPathValidate)) 451 { 452 // Finally, assign in proper format. 453 FileDialog *pDlg = static_cast<FileDialog*>(object); 454 pDlg->mData.mDefaultPath = StringTable->insert(szPathValidate); 455 } 456#ifdef TORQUE_DEBUG 457 else 458 Con::errorf(ConsoleLogEntry::GUI, "FileDialog - Invalid Default Path Specified!"); 459#endif 460 461 return false; 462 463}; 464 465//----------------------------------------------------------------------------- 466// Default File Property - String Validated on Write 467//----------------------------------------------------------------------------- 468bool FileDialog::setDefaultFile(void *object, const char *index, const char *data) 469{ 470 if (!data || !dStrncmp(data, "", 1)) 471 return true; 472 473 // Copy and Backslash the path (Windows dialogs are VERY picky about this format) 474 static char szPathValidate[512]; 475 Platform::makeFullPathName(data, szPathValidate, sizeof(szPathValidate)); 476 //backslash( szPathValidate ); 477 478 // Remove any trailing \'s 479 S8 validateLen = dStrlen(szPathValidate); 480 if (szPathValidate[validateLen - 1] == '\\') 481 szPathValidate[validateLen - 1] = '\0'; 482 483 // Finally, assign in proper format. 484 FileDialog *pDlg = static_cast<FileDialog*>(object); 485 pDlg->mData.mDefaultFile = StringTable->insert(szPathValidate); 486 487 return false; 488}; 489 490//----------------------------------------------------------------------------- 491// ChangePath Property - Change working path on successful file selection 492//----------------------------------------------------------------------------- 493bool FileDialog::setChangePath(void *object, const char *index, const char *data) 494{ 495 bool bMustExist = dAtob(data); 496 497 FileDialog *pDlg = static_cast<FileDialog*>(object); 498 499 if (bMustExist) 500 pDlg->mData.mStyle |= FileDialogData::FDS_CHANGEPATH; 501 else 502 pDlg->mData.mStyle &= ~<a href="/coding/class/structfiledialogdata/">FileDialogData</a>::FDS_CHANGEPATH; 503 504 return true; 505}; 506 507const char* FileDialog::getChangePath(void* obj, const char* data) 508{ 509 FileDialog *pDlg = static_cast<FileDialog*>(obj); 510 if (pDlg->mData.mStyle & FileDialogData::FDS_CHANGEPATH) 511 return StringTable->insert("true"); 512 else 513 return StringTable->insert("false"); 514} 515 516bool FileDialog::setFile(void *object, const char *index, const char *data) 517{ 518 return false; 519}; 520 521//----------------------------------------------------------------------------- 522// OpenFileDialog Implementation 523//----------------------------------------------------------------------------- 524 525ConsoleDocClass(OpenFileDialog, 526 "@brief Derived from FileDialog, this class is responsible for opening a file browser with the intention of opening a file.\n\n" 527 528 "The core usage of this dialog is to locate a file in the OS and return the path and name. This does not handle " 529 "the actual file parsing or data manipulation. That functionality is left up to the FileObject class.\n\n" 530 531 "@tsexample\n" 532 " // Create a dialog dedicated to opening files\n" 533 " %openFileDlg = new OpenFileDialog()\n" 534 " {\n" 535 " // Look for jpg image files\n" 536 " // First part is the descriptor|second part is the extension\n" 537 " Filters = \"Jepg Files|*.jpg\";\n" 538 " // Allow browsing through other folders\n" 539 " ChangePath = true;\n\n" 540 " // Only allow opening of one file at a time\n" 541 " MultipleFiles = false;\n" 542 " };\n\n" 543 " // Launch the open file dialog\n" 544 " %result = %openFileDlg.Execute();\n\n" 545 " // Obtain the chosen file name and path\n" 546 " if ( %result )\n" 547 " {\n" 548 " %seletedFile = %openFileDlg.file;\n" 549 " }\n" 550 " else\n" 551 " {\n" 552 " %selectedFile = \"\";\n" 553 " }\n\n" 554 " // Cleanup\n" 555 " %openFileDlg.delete();\n\n\n" 556 "@endtsexample\n\n" 557 558 "@note FileDialog and its related classes are only availble in a Tools build of Torque.\n\n" 559 560 "@see FileDialog\n" 561 "@see SaveFileDialog\n" 562 "@see FileObject\n" 563 564 "@ingroup FileSystem\n" 565 ); 566OpenFileDialog::OpenFileDialog() 567{ 568 // Default File Must Exist 569 mData.mStyle = FileDialogData::FDS_OPEN | FileDialogData::FDS_MUSTEXIST; 570} 571 572OpenFileDialog::~OpenFileDialog() 573{ 574 mMustExist = true; 575 mMultipleFiles = false; 576} 577 578IMPLEMENT_CONOBJECT(OpenFileDialog); 579 580//----------------------------------------------------------------------------- 581// Console Properties 582//----------------------------------------------------------------------------- 583void OpenFileDialog::initPersistFields() 584{ 585 addProtectedField("MustExist", TypeBool, Offset(mMustExist, OpenFileDialog), &setMustExist, &getMustExist, "True/False whether the file returned must exist or not"); 586 addProtectedField("MultipleFiles", TypeBool, Offset(mMultipleFiles, OpenFileDialog), &setMultipleFiles, &getMultipleFiles, "True/False whether multiple files may be selected and returned or not"); 587 588 Parent::initPersistFields(); 589} 590 591//----------------------------------------------------------------------------- 592// File Must Exist - Boolean 593//----------------------------------------------------------------------------- 594bool OpenFileDialog::setMustExist(void *object, const char *index, const char *data) 595{ 596 bool bMustExist = dAtob(data); 597 598 OpenFileDialog *pDlg = static_cast<OpenFileDialog*>(object); 599 600 if (bMustExist) 601 pDlg->mData.mStyle |= FileDialogData::FDS_MUSTEXIST; 602 else 603 pDlg->mData.mStyle &= ~<a href="/coding/class/structfiledialogdata/">FileDialogData</a>::FDS_MUSTEXIST; 604 605 return true; 606}; 607 608const char* OpenFileDialog::getMustExist(void* obj, const char* data) 609{ 610 OpenFileDialog *pDlg = static_cast<OpenFileDialog*>(obj); 611 if (pDlg->mData.mStyle & FileDialogData::FDS_MUSTEXIST) 612 return StringTable->insert("true"); 613 else 614 return StringTable->insert("false"); 615} 616 617//----------------------------------------------------------------------------- 618// Can Select Multiple Files - Boolean 619//----------------------------------------------------------------------------- 620bool OpenFileDialog::setMultipleFiles(void *object, const char *index, const char *data) 621{ 622 bool bMustExist = dAtob(data); 623 624 OpenFileDialog *pDlg = static_cast<OpenFileDialog*>(object); 625 626 if (bMustExist) 627 pDlg->mData.mStyle |= FileDialogData::FDS_MULTIPLEFILES; 628 else 629 pDlg->mData.mStyle &= ~<a href="/coding/class/structfiledialogdata/">FileDialogData</a>::FDS_MULTIPLEFILES; 630 631 return true; 632}; 633 634const char* OpenFileDialog::getMultipleFiles(void* obj, const char* data) 635{ 636 OpenFileDialog *pDlg = static_cast<OpenFileDialog*>(obj); 637 if (pDlg->mData.mStyle & FileDialogData::FDS_MULTIPLEFILES) 638 return StringTable->insert("true"); 639 else 640 return StringTable->insert("false"); 641} 642 643//----------------------------------------------------------------------------- 644// SaveFileDialog Implementation 645//----------------------------------------------------------------------------- 646ConsoleDocClass(SaveFileDialog, 647 "@brief Derived from FileDialog, this class is responsible for opening a file browser with the intention of saving a file.\n\n" 648 649 "The core usage of this dialog is to locate a file in the OS and return the path and name. This does not handle " 650 "the actual file writing or data manipulation. That functionality is left up to the FileObject class.\n\n" 651 652 "@tsexample\n" 653 " // Create a dialog dedicated to opening file\n" 654 " %saveFileDlg = new SaveFileDialog()\n" 655 " {\n" 656 " // Only allow for saving of COLLADA files\n" 657 " Filters = \"COLLADA Files (*.dae)|*.dae|\";\n\n" 658 " // Default save path to where the WorldEditor last saved\n" 659 " DefaultPath = $pref::WorldEditor::LastPath;\n\n" 660 " // No default file specified\n" 661 " DefaultFile = \"\";\n\n" 662 " // Do not allow the user to change to a new directory\n" 663 " ChangePath = false;\n\n" 664 " // Prompt the user if they are going to overwrite an existing file\n" 665 " OverwritePrompt = true;\n" 666 " };\n\n" 667 " // Launch the save file dialog\n" 668 " %saveFileDlg.Execute();\n\n" 669 " if ( %result )\n" 670 " {\n" 671 " %seletedFile = %openFileDlg.file;\n" 672 " }\n" 673 " else\n" 674 " {\n" 675 " %selectedFile = \"\";\n" 676 " }\n\n" 677 " // Cleanup\n" 678 " %saveFileDlg.delete();\n" 679 "@endtsexample\n\n" 680 681 "@note FileDialog and its related classes are only availble in a Tools build of Torque.\n\n" 682 683 "@see FileDialog\n" 684 "@see OpenFileDialog\n" 685 "@see FileObject\n" 686 687 "@ingroup FileSystem\n" 688 ); 689SaveFileDialog::SaveFileDialog() 690{ 691 // Default File Must Exist 692 mData.mStyle = FileDialogData::FDS_SAVE | FileDialogData::FDS_OVERWRITEPROMPT; 693 mOverwritePrompt = true; 694} 695 696SaveFileDialog::~SaveFileDialog() 697{ 698} 699 700IMPLEMENT_CONOBJECT(SaveFileDialog); 701 702//----------------------------------------------------------------------------- 703// Console Properties 704//----------------------------------------------------------------------------- 705void SaveFileDialog::initPersistFields() 706{ 707 addProtectedField("OverwritePrompt", TypeBool, Offset(mOverwritePrompt, SaveFileDialog), &setOverwritePrompt, &getOverwritePrompt, "True/False whether the dialog should prompt before accepting an existing file name"); 708 709 Parent::initPersistFields(); 710} 711 712//----------------------------------------------------------------------------- 713// Prompt on Overwrite - Boolean 714//----------------------------------------------------------------------------- 715bool SaveFileDialog::setOverwritePrompt(void *object, const char *index, const char *data) 716{ 717 bool bMustExist = dAtob(data); 718 719 SaveFileDialog *pDlg = static_cast<SaveFileDialog*>(object); 720 721 if (bMustExist) 722 pDlg->mData.mStyle |= FileDialogData::FDS_OVERWRITEPROMPT; 723 else 724 pDlg->mData.mStyle &= ~<a href="/coding/class/structfiledialogdata/">FileDialogData</a>::FDS_OVERWRITEPROMPT; 725 726 return true; 727}; 728 729const char* SaveFileDialog::getOverwritePrompt(void* obj, const char* data) 730{ 731 SaveFileDialog *pDlg = static_cast<SaveFileDialog*>(obj); 732 if (pDlg->mData.mStyle & FileDialogData::FDS_OVERWRITEPROMPT) 733 return StringTable->insert("true"); 734 else 735 return StringTable->insert("false"); 736} 737 738//----------------------------------------------------------------------------- 739// OpenFolderDialog Implementation 740//----------------------------------------------------------------------------- 741 742OpenFolderDialog::OpenFolderDialog() 743{ 744 mData.mStyle = FileDialogData::FDS_OPEN | FileDialogData::FDS_OVERWRITEPROMPT | FileDialogData::FDS_BROWSEFOLDER; 745 746 mMustExistInDir = ""; 747} 748 749IMPLEMENT_CONOBJECT(OpenFolderDialog); 750 751ConsoleDocClass(OpenFolderDialog, 752 "@brief OS level dialog used for browsing folder structures.\n\n" 753 754 "This is essentially an OpenFileDialog, but only used for returning directory paths, not files.\n\n" 755 756 "@note FileDialog and its related classes are only availble in a Tools build of Torque.\n\n" 757 758 "@see OpenFileDialog for more details on functionality.\n\n" 759 760 "@ingroup FileSystem\n" 761 ); 762 763void OpenFolderDialog::initPersistFields() 764{ 765 addField("fileMustExist", TypeFilename, Offset(mMustExistInDir, OpenFolderDialog), "File that must be in selected folder for it to be valid"); 766 767 Parent::initPersistFields(); 768} 769#endif 770