fileDialog.cpp

Engine/source/platform/nativeDialogs/fileDialog.cpp

More...

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