fileDialog.cpp 27 KB

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