diff --git a/indra/llplugin/llpluginmessagepipe.cpp b/indra/llplugin/llpluginmessagepipe.cpp index ebac3c52b..7aaa09372 100644 --- a/indra/llplugin/llpluginmessagepipe.cpp +++ b/indra/llplugin/llpluginmessagepipe.cpp @@ -89,6 +89,16 @@ bool LLPluginMessagePipeOwner::writeMessageRaw(const std::string &message) return result; } +bool LLPluginMessagePipeOwner::flushMessages(void) +{ + bool result = true; + if (mMessagePipe != NULL) + { + result = mMessagePipe->flushMessages(); + } + return result; +} + void LLPluginMessagePipeOwner::killMessagePipe(void) { if(mMessagePipe != NULL) @@ -163,26 +173,32 @@ bool LLPluginMessagePipe::pump(F64 timeout) return result; } -bool LLPluginMessagePipe::pumpOutput() +static apr_interval_time_t const flush_max_block_time = 10000000; // Even when flushing, give up after 10 seconds. +static apr_interval_time_t const flush_min_timeout = 1000; // When flushing, initially timeout after 1 ms. +static apr_interval_time_t const flush_max_timeout = 50000; // Never wait longer than 50 ms. + +// DO NOT SET 'flush' TO TRUE WHEN CALLED ON THE VIEWER SIDE! +// flush is only intended for plugin-side. +bool LLPluginMessagePipe::pumpOutput(bool flush) { bool result = true; if(mSocket) { - apr_status_t status; - apr_size_t size; + apr_interval_time_t flush_time_left_usec = flush_max_block_time; + apr_interval_time_t timeout_usec = flush ? flush_min_timeout : 0; LLMutexLock lock(&mOutputMutex); - if(!mOutput.empty()) + while(result && !mOutput.empty()) { // write any outgoing messages - size = (apr_size_t)mOutput.size(); + apr_size_t size = (apr_size_t)mOutput.size(); - setSocketTimeout(0); + setSocketTimeout(timeout_usec); // LL_INFOS("Plugin") << "before apr_socket_send, size = " << size << LL_ENDL; - status = apr_socket_send( + apr_status_t status = apr_socket_send( mSocket->getSocket(), (const char*)mOutput.data(), &size); @@ -193,12 +209,29 @@ bool LLPluginMessagePipe::pumpOutput() { // success mOutput = mOutput.substr(size); + break; } - else if(APR_STATUS_IS_EAGAIN(status)) + else if(APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) { // Socket buffer is full... // remove the written part from the buffer and try again later. mOutput = mOutput.substr(size); + if (!flush) + break; + flush_time_left_usec -= timeout_usec; + if (flush_time_left_usec <= 0) + { + result = false; + } + else if (size == 0) + { + // Nothing at all was written. Increment wait time. + timeout_usec = std::min(flush_max_timeout, 2 * timeout_usec); + } + else + { + timeout_usec = std::max(flush_min_timeout, timeout_usec / 2); + } } else if(APR_STATUS_IS_EOF(status)) { diff --git a/indra/llplugin/llpluginmessagepipe.h b/indra/llplugin/llpluginmessagepipe.h index 6eedca27f..1e71caf42 100644 --- a/indra/llplugin/llpluginmessagepipe.h +++ b/indra/llplugin/llpluginmessagepipe.h @@ -61,6 +61,8 @@ protected: bool canSendMessage(void); // call this to send a message over the pipe bool writeMessageRaw(const std::string &message); + // call this to attempt to flush all messages for 10 seconds long. + bool flushMessages(void); // call this to close the pipe void killMessagePipe(void); @@ -79,8 +81,10 @@ public: void clearOwner(void); bool pump(F64 timeout = 0.0f); - bool pumpOutput(); + bool pumpOutput(bool flush = false); bool pumpInput(F64 timeout = 0.0f); + + bool flushMessages(void) { return pumpOutput(true); } protected: void processInput(void); diff --git a/indra/llplugin/llpluginprocesschild.cpp b/indra/llplugin/llpluginprocesschild.cpp index 3ba765bf6..fdcb4f6a8 100644 --- a/indra/llplugin/llpluginprocesschild.cpp +++ b/indra/llplugin/llpluginprocesschild.cpp @@ -556,11 +556,22 @@ void LLPluginProcessChild::receivePluginMessage(const std::string &message) } else if (message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL) { + bool flush = false; std::string message_name = parsed.getName(); if(message_name == "shutdown") { // The plugin is finished. setState(STATE_UNLOADING); + flush = true; + } + else if (message_name == "flush") + { + flush = true; + passMessage = false; + } + if (flush) + { + flushMessages(); } } } diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp index fc94ee849..0d361324e 100644 --- a/indra/llplugin/llpluginprocessparent.cpp +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -104,6 +104,7 @@ LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner) mDebug = false; mBlocked = false; mPolledInput = false; + mReceivedShutdown = false; mPollFD.client_data = NULL; mPollFDPool.create(); @@ -165,6 +166,8 @@ void LLPluginProcessParent::errorState(void) { if(mState < STATE_RUNNING) setState(STATE_LAUNCH_FAILURE); + else if (mReceivedShutdown) + setState(STATE_EXITING); else setState(STATE_ERROR); } @@ -1012,6 +1015,7 @@ void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message) else if(message_name == "shutdown") { LL_INFOS("Plugin") << "received shutdown message" << LL_ENDL; + mReceivedShutdown = true; mOwner->receivedShutdown(); } else if(message_name == "shm_add_response") diff --git a/indra/llplugin/llpluginprocessparent.h b/indra/llplugin/llpluginprocessparent.h index 1ec5ef4c3..65455950f 100644 --- a/indra/llplugin/llpluginprocessparent.h +++ b/indra/llplugin/llpluginprocessparent.h @@ -145,8 +145,8 @@ private: STATE_RUNNING, // STATE_LAUNCH_FAILURE, // Failure before plugin loaded STATE_ERROR, // generic bailout state - STATE_CLEANUP, // clean everything up STATE_EXITING, // Tried to kill process, waiting for it to exit + STATE_CLEANUP, // clean everything up STATE_DONE // }; @@ -183,6 +183,7 @@ private: bool mDebug; bool mBlocked; bool mPolledInput; + bool mReceivedShutdown; LLProcessLauncher mDebugger; diff --git a/indra/newview/statemachine/aifilepicker.cpp b/indra/newview/statemachine/aifilepicker.cpp index e3a09009d..f8da4da86 100644 --- a/indra/newview/statemachine/aifilepicker.cpp +++ b/indra/newview/statemachine/aifilepicker.cpp @@ -64,31 +64,41 @@ AIFilePicker::AIFilePicker(void) : mPluginManager(NULL), mCanceled(false) { } +// static +AITHREADSAFESIMPLE(AIFilePicker::context_map_type, AIFilePicker::sContextMap, ); + +// static void AIFilePicker::store_folder(std::string const& context, std::string const& folder) { if (!folder.empty()) { - mContextMap[context] = folder; + LL_DEBUGS("Plugin") << "AIFilePicker::mContextMap[\"" << context << "\"] = \"" << folder << '"' << LL_ENDL; + AIAccess(sContextMap)->operator[](context) = folder; } } +// static std::string AIFilePicker::get_folder(std::string const& default_path, std::string const& context) { - context_map_type::iterator iter = mContextMap.find(context); - if (iter != mContextMap.end()) + AIAccess wContextMap(sContextMap); + context_map_type::iterator iter = wContextMap->find(context); + if (iter != wContextMap->end()) { return iter->second; } else if (!default_path.empty()) { + LL_DEBUGS("Plugin") << "context \"" << context << "\" not found. Returning default_path \"" << default_path << "\"." << LL_ENDL; return default_path; } - else if ((iter = mContextMap.find("savefile")) != mContextMap.end()) + else if ((iter = wContextMap->find("savefile")) != wContextMap->end()) { + LL_DEBUGS("Plugin") << "context \"" << context << "\" not found and default_path empty. Returning context \"savefile\": \"" << iter->second << "\"." << LL_ENDL; return iter->second; } else { + LL_DEBUGS("Plugin") << "context \"" << context << "\" not found, default_path empty and context \"savefile\" not found. Returning \"$HOME\"." << LL_ENDL; // This is the last resort when all else failed. Open the file chooser in directory 'home'. char const* home = NULL; #if LL_WINDOWS @@ -103,7 +113,7 @@ std::string AIFilePicker::get_folder(std::string const& default_path, std::strin void AIFilePicker::open(ELoadFilter filter, std::string const& default_path, std::string const& context, bool multiple) { mContext = context; - mFolder = get_folder(default_path, context); + mFolder = AIFilePicker::get_folder(default_path, context); mOpenType = multiple ? load_multiple : load; switch(filter) { @@ -152,7 +162,7 @@ void AIFilePicker::open(std::string const& filename, ESaveFilter filter, std::st { mFilename = filename; mContext = context; - mFolder = get_folder(default_path, context); + mFolder = AIFilePicker::get_folder(default_path, context); mOpenType = save; switch(filter) { @@ -372,7 +382,7 @@ void AIFilePicker::multiplex_impl(void) case AIFilePicker_done: { // Store folder of first filename as context. - store_folder(mContext, getFolder()); + AIFilePicker::store_folder(mContext, getFolder()); finish(); } } diff --git a/indra/newview/statemachine/aifilepicker.h b/indra/newview/statemachine/aifilepicker.h index a1709eeb8..91a7bc819 100644 --- a/indra/newview/statemachine/aifilepicker.h +++ b/indra/newview/statemachine/aifilepicker.h @@ -176,9 +176,8 @@ public: private: LLPointer mPluginManager; //!< Pointer to the plugin manager. - // AIFIXME: this should be a separate, thread-safe singleton. typedef std::map context_map_type; //!< Type of mContextMap. - context_map_type mContextMap; //!< Map context (ie, "snapshot" or "image") to last used folder. + static AIThreadSafeSimple sContextMap; //!< Map context (ie, "snapshot" or "image") to last used folder. std::string mContext; //!< Some key to indicate the context (remembers the folder per key). // Input variables (cache variable between call to open and run). @@ -191,9 +190,9 @@ private: std::vector mFilenames; //!< Filesnames. // Store a folder for the given context. - void store_folder(std::string const& context, std::string const& folder); + static void store_folder(std::string const& context, std::string const& folder); // Return the last folder stored for 'context', or default_path if none, or context "savefile" if empty, or $HOME if none. - std::string get_folder(std::string const& default_path, std::string const& context); + static std::string get_folder(std::string const& default_path, std::string const& context); protected: // Call finish() (or abort()), not delete. diff --git a/indra/plugins/base_basic/basic_plugin_base.cpp b/indra/plugins/base_basic/basic_plugin_base.cpp index f791b5887..d1a656391 100755 --- a/indra/plugins/base_basic/basic_plugin_base.cpp +++ b/indra/plugins/base_basic/basic_plugin_base.cpp @@ -115,6 +115,20 @@ void BasicPluginBase::sendLogMessage(std::string const& message, LLPluginMessage logmessage.setValue("message", message); logmessage.setValueS32("log_level",level); mSendMessageFunction(logmessage.generate().c_str(), &mPluginInstance); + // Theoretically we should flush here (that's what PLS_ENDL means), but we don't because + // that interfers with how the code would act without debug messages. Instead, the developer + // is responsible to call flushMessages themselves at the appropriate places. +} + +/** + * Flush all messages to the viewer. + * + * This blocks if necessary, up till 10 seconds, before it gives up. + */ +void BasicPluginBase::flushMessages(void) +{ + LLPluginMessage logmessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "flush"); + mSendMessageFunction(logmessage.generate().c_str(), &mPluginInstance); } /** @@ -124,6 +138,9 @@ void BasicPluginBase::sendLogMessage(std::string const& message, LLPluginMessage */ void BasicPluginBase::sendShutdownMessage(void) { + // Do a double flush. First flush all messages so far, then send the shutdown message, + // which also will try to flush itself before terminating the process. + flushMessages(); LLPluginMessage shutdownmessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shutdown"); sendMessage(shutdownmessage); } diff --git a/indra/plugins/base_basic/basic_plugin_base.h b/indra/plugins/base_basic/basic_plugin_base.h index 492b408e7..042a27b63 100755 --- a/indra/plugins/base_basic/basic_plugin_base.h +++ b/indra/plugins/base_basic/basic_plugin_base.h @@ -36,16 +36,13 @@ #ifndef BASIC_PLUGIN_BASE_H #define BASIC_PLUGIN_BASE_H -#include - -#include - #include "linden_common.h" - #include "llplugininstance.h" #include "llpluginmessage.h" #include "llpluginmessageclasses.h" +#include + class BasicPluginBase { public: @@ -65,7 +62,10 @@ public: // Used for log messages. Use macros below. void sendLogMessage(std::string const& message, LLPluginMessage::LLPLUGIN_LOG_LEVEL level); - // Shoot down the whole process. + // Flush all messages to the viewer. + void flushMessages(void); + + // Flush all messages and then shoot down the whole process. void sendShutdownMessage(void); protected: @@ -127,6 +127,7 @@ int create_plugin( #define PLS_INFOS PLS_LOG_MESSAGE(LLPluginMessage::LOG_LEVEL_INFO, LL_DEBUG_PLUGIN_MESSAGES) #define PLS_WARNS PLS_LOG_MESSAGE(LLPluginMessage::LOG_LEVEL_WARN, 1) #define PLS_ERRS PLS_LOG_MESSAGE(LLPluginMessage::LOG_LEVEL_ERR, 1) +#define PLS_FLUSH do { BasicPluginBase::sPluginBase->flushMessages(); } while(0) #endif // BASIC_PLUGIN_BASE diff --git a/indra/plugins/filepicker/basic_plugin_filepicker.cpp b/indra/plugins/filepicker/basic_plugin_filepicker.cpp index 1201c3f71..7e3a4f0b0 100644 --- a/indra/plugins/filepicker/basic_plugin_filepicker.cpp +++ b/indra/plugins/filepicker/basic_plugin_filepicker.cpp @@ -232,10 +232,13 @@ void FilepickerPlugin::receiveMessage(char const* message_string) std::string folder = message_in.getValue("folder"); bool get_directory = (filter == "directory"); + // We about to completely block on running the modal File/Dir picker window, so flush any pending messages to the viewer. + flushMessages(); + bool canceled; if (get_directory) { - canceled = !LLDirPicker::instance().getDir(&folder); + canceled = !LLDirPicker::instance().getDir(folder); } else if (type == "save") { @@ -275,6 +278,7 @@ void FilepickerPlugin::receiveMessage(char const* message_string) sendMessage(message); } // We're done. Exit the whole application. + // This first flushes any messages before terminating the plugin. sendShutdownMessage(); } else diff --git a/indra/plugins/filepicker/legacy.cpp b/indra/plugins/filepicker/legacy.cpp index f89862061..e106923bf 100644 --- a/indra/plugins/filepicker/legacy.cpp +++ b/indra/plugins/filepicker/legacy.cpp @@ -28,8 +28,8 @@ * - Initial version, written by Aleric Inglewood @ SL */ -#include "legacy.h" #include "basic_plugin_base.h" // For PLS_INFOS etc. +#include "legacy.h" // Translation map. translation_map_type translation_map; diff --git a/indra/plugins/filepicker/lldirpicker.cpp b/indra/plugins/filepicker/lldirpicker.cpp index 39dff05e9..7ddcda6df 100644 --- a/indra/plugins/filepicker/lldirpicker.cpp +++ b/indra/plugins/filepicker/lldirpicker.cpp @@ -65,7 +65,15 @@ LLDirPicker::~LLDirPicker() // nothing } -BOOL LLDirPicker::getDir(std::string* filename) +static int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) +{ + if (uMsg == BFFM_INITIALIZED) + { + SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData); + } +} + +BOOL LLDirPicker::getDir(std::string const& folder) { if( mLocked ) { @@ -73,34 +81,38 @@ BOOL LLDirPicker::getDir(std::string* filename) } BOOL success = FALSE; - BROWSEINFO bi; - memset(&bi, 0, sizeof(bi)); + BROWSEINFO bi; + memset(&bi, 0, sizeof(bi)); - bi.ulFlags = BIF_USENEWUI; - bi.hwndOwner = (HWND)gViewerWindow->getPlatformWindow(); - bi.lpszTitle = NULL; + bi.ulFlags = BIF_USENEWUI; + bi.hwndOwner = (HWND)gViewerWindow->getPlatformWindow(); + bi.lpszTitle = NULL; + llutf16string tstring = utf8str_to_utf16str(folder); + bi.lParam = (LPARAM)tstring.c_str(); + bi.lpfn = &BrowseCallbackProc; - ::OleInitialize(NULL); + ::OleInitialize(NULL); - LPITEMIDLIST pIDL = ::SHBrowseForFolder(&bi); + PLS_FLUSH; + LPITEMIDLIST pIDL = ::SHBrowseForFolder(&bi); - if(pIDL != NULL) - { - WCHAR buffer[_MAX_PATH] = {'\0'}; + if(pIDL != NULL) + { + WCHAR buffer[_MAX_PATH] = {'\0'}; - if(::SHGetPathFromIDList(pIDL, buffer) != 0) - { - // Set the string value. + if(::SHGetPathFromIDList(pIDL, buffer) != 0) + { + // Set the string value. - mDir = utf16str_to_utf8str(llutf16string(buffer)); - success = TRUE; - } + mDir = utf16str_to_utf8str(llutf16string(buffer)); + success = TRUE; + } - // free the item id list - CoTaskMemFree(pIDL); - } + // free the item id list + CoTaskMemFree(pIDL); + } - ::OleUninitialize(); + ::OleUninitialize(); return success; } @@ -147,9 +159,9 @@ pascal void LLDirPicker::doNavCallbackEvent(NavEventCallbackMessage callBackSele //Convert string to a FSSpec FSRef myFSRef; - const char* filename=sInstance.mFileName->c_str(); + const char* folder = sInstance.mFileName->c_str(); - error = FSPathMakeRef ((UInt8*)filename, &myFSRef, NULL); + error = FSPathMakeRef ((UInt8*)folder, &myFSRef, NULL); if (error != noErr) break; @@ -185,7 +197,10 @@ OSStatus LLDirPicker::doNavChooseDialog() gViewerWindow->mWindow->beforeDialog(); if (error == noErr) + { + PLS_FLUSH; error = NavDialogRun(navRef); + } gViewerWindow->mWindow->afterDialog(); @@ -216,13 +231,13 @@ OSStatus LLDirPicker::doNavChooseDialog() return error; } -BOOL LLDirPicker::getDir(std::string* filename) +BOOL LLDirPicker::getDir(std::string const& folder) { if( mLocked ) return FALSE; BOOL success = FALSE; OSStatus error = noErr; - mFileName = filename; + mFileName = folder; // mNavOptions.saveFileName @@ -266,14 +281,15 @@ void LLDirPicker::reset() LLFilePickerBase::reset(); } -BOOL LLDirPicker::getDir(std::string* filename) +BOOL LLDirPicker::getDir(std::string const& folder) { reset(); - GtkWindow* picker = buildFilePicker(false, true, "dirpicker"); + GtkWindow* picker = buildFilePicker(false, true, folder); if (picker) { gtk_window_set_title(GTK_WINDOW(picker), LLTrans::getString("choose_the_directory").c_str()); gtk_widget_show_all(GTK_WIDGET(picker)); + PLS_FLUSH; gtk_main(); return (!getFirstFile().empty()); } @@ -301,7 +317,7 @@ void LLDirPicker::reset() { } -BOOL LLDirPicker::getDir(std::string* filename) +BOOL LLDirPicker::getDir(std::string* folder) { return FALSE; } diff --git a/indra/plugins/filepicker/lldirpicker.h b/indra/plugins/filepicker/lldirpicker.h index cdee1d094..034bcdf91 100644 --- a/indra/plugins/filepicker/lldirpicker.h +++ b/indra/plugins/filepicker/lldirpicker.h @@ -71,7 +71,7 @@ public: // calling this before main() is undefined static LLDirPicker& instance( void ) { return sInstance; } - BOOL getDir(std::string* filename); + BOOL getDir(std::string const& filename); std::string getDirName(); // clear any lists of buffers or whatever, and make sure the dir diff --git a/indra/plugins/filepicker/llfilepicker.cpp b/indra/plugins/filepicker/llfilepicker.cpp index 48069e88c..066a8340c 100644 --- a/indra/plugins/filepicker/llfilepicker.cpp +++ b/indra/plugins/filepicker/llfilepicker.cpp @@ -205,7 +205,6 @@ bool LLFilePickerBase::setupFilter(ELoadFilter filter) return res; } -// AIFIXME: Use folder bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder) { if( mLocked ) @@ -214,9 +213,9 @@ bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder } bool success = FALSE; - // don't provide default file selection + llutf16string tstring = utf8str_to_utf16str(folder); + mOFN.lpstrInitialDir = (LPCTSTR)tstring.data(); mFilesW[0] = '\0'; - mOFN.lpstrFile = mFilesW; mOFN.nMaxFile = SINGLE_FILENAME_BUFFER_SIZE; mOFN.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR ; @@ -237,7 +236,6 @@ bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder return success; } -// AIFIXME: Use folder bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string const& folder) { if( mLocked ) @@ -246,9 +244,9 @@ bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string cons } bool success = FALSE; - // don't provide default file selection + llutf16string tstring = utf8str_to_utf16str(folder); + mOFN.lpstrInitialDir = (LPCTSTR)tstring.data(); mFilesW[0] = '\0'; - mOFN.lpstrFile = mFilesW; mOFN.nFilterIndex = 1; mOFN.nMaxFile = FILENAME_BUFFER_SIZE; @@ -295,7 +293,6 @@ bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string cons return success; } -// AIFIXME: Use folder bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filename, std::string const& folder) { if( mLocked ) @@ -304,11 +301,13 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena } bool success = FALSE; + llutf16string tstring = utf8str_to_utf16str(folder); + mOFN.lpstrInitialDir = (LPCTSTR)tstring.data(); mOFN.lpstrFile = mFilesW; if (!filename.empty()) { llutf16string tstring = utf8str_to_utf16str(filename); - wcsncpy(mFilesW, tstring.c_str(), FILENAME_BUFFER_SIZE); } /*Flawfinder: ignore*/ + wcsncpy(mFilesW, tstring.data(), FILENAME_BUFFER_SIZE); } /*Flawfinder: ignore*/ else { mFilesW[0] = '\0'; @@ -705,8 +704,9 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena Boolean LLFilePickerBase::navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode) { + LLFilePickerBase* picker = (LLFilePickerBase*)callBackUD; Boolean result = true; - ELoadFilter filter = *((ELoadFilter*) callBackUD); + ELoadFilter filter = picker->getLoadFilter(); OSStatus error = noErr; if (filterMode == kNavFilteringBrowserList && filter != FFLOAD_ALL && (theItem->descriptorType == typeFSRef || theItem->descriptorType == typeFSS)) @@ -788,7 +788,7 @@ Boolean LLFilePickerBase::navOpenFilterProc(AEDesc *theItem, void *info, void *c } else if (filter == FFLOAD_RAW) { - if (fileInfo.filetype != '\?\?\?\?' && + if (fileInfo.filetype != L'\?\?\?\?' && (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("raw"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)) ) { @@ -809,23 +809,70 @@ Boolean LLFilePickerBase::navOpenFilterProc(AEDesc *theItem, void *info, void *c return result; } -OSStatus LLFilePickerBase::doNavChooseDialog(ELoadFilter filter) +//static +pascal void LLFilePickerBase::doNavCallbackEvent(NavEventCallbackMessage callBackSelector, + NavCBRecPtr callBackParms, void* callBackUD) +{ + switch(callBackSelector) + { + case kNavCBStart: + { + LLFilePickerBase* picker = reinterpret_cast(callBackUD); + if (picker->getFolder().empty()) break; + + OSStatus error = noErr; + AEDesc theLocation = {typeNull, NULL}; + FSSpec outFSSpec; + + //Convert string to a FSSpec + FSRef myFSRef; + + const char* folder = picker->getFolder().c_str(); + + error = FSPathMakeRef ((UInt8*)folder, &myFSRef, NULL); + + if (error != noErr) break; + + error = FSGetCatalogInfo (&myFSRef, kFSCatInfoNone, NULL, NULL, &outFSSpec, NULL); + + if (error != noErr) break; + + error = AECreateDesc(typeFSS, &outFSSpec, sizeof(FSSpec), &theLocation); + + if (error != noErr) break; + + error = NavCustomControl(callBackParms->context, + kNavCtlSetLocation, (void*)&theLocation); + + } + } +} + +OSStatus LLFilePickerBase::doNavChooseDialog(ELoadFilter filter, std::string const& folder) { OSStatus error = noErr; NavDialogRef navRef = NULL; NavReplyRecord navReply; memset(&navReply, 0, sizeof(navReply)); + + mLoadFilter = filter; + mFolder = folder; + + NavEventUPP eventProc = NewNavEventUPP(doNavCallbackEvent); // NOTE: we are passing the address of a local variable here. // This is fine, because the object this call creates will exist for less than the lifetime of this function. // (It is destroyed by NavDialogDispose() below.) - error = NavCreateChooseFileDialog(&mNavOptions, NULL, NULL, NULL, navOpenFilterProc, (void*)(&filter), &navRef); + error = NavCreateChooseFileDialog(&mNavOptions, NULL, eventProc, NULL, navOpenFilterProc, (void*)this, &navRef); //gViewerWindow->mWindow->beforeDialog(); if (error == noErr) + { + PLS_FLUSH; error = NavDialogRun(navRef); + } //gViewerWindow->mWindow->afterDialog(); @@ -860,11 +907,14 @@ OSStatus LLFilePickerBase::doNavChooseDialog(ELoadFilter filter) mFiles.push_back(std::string(path)); } } + + if (eventProc) + DisposeNavEventUPP(eventProc); return error; } -OSStatus LLFilePickerBase::doNavSaveDialog(ESaveFilter filter, const std::string& filename) +OSStatus LLFilePickerBase::doNavSaveDialog(ESaveFilter filter, std::string const& filename, std::string const& folder) { OSStatus error = noErr; NavDialogRef navRef = NULL; @@ -905,46 +955,49 @@ OSStatus LLFilePickerBase::doNavSaveDialog(ESaveFilter filter, const std::string extension = CFSTR(".png"); break; case FFSAVE_AVI: - type = '\?\?\?\?'; - creator = '\?\?\?\?'; + type = L'\?\?\?\?'; + creator = L'\?\?\?\?'; extension = CFSTR(".mov"); break; case FFSAVE_ANIM: - type = '\?\?\?\?'; - creator = '\?\?\?\?'; + type = L'\?\?\?\?'; + creator = L'\?\?\?\?'; extension = CFSTR(".xaf"); break; #ifdef _CORY_TESTING case FFSAVE_GEOMETRY: - type = '\?\?\?\?'; - creator = '\?\?\?\?'; + type = L'\?\?\?\?'; + creator = L'\?\?\?\?'; extension = CFSTR(".slg"); break; #endif case FFSAVE_RAW: - type = '\?\?\?\?'; - creator = '\?\?\?\?'; + type = L'\?\?\?\?'; + creator = L'\?\?\?\?'; extension = CFSTR(".raw"); break; case FFSAVE_J2C: - type = '\?\?\?\?'; + type = L'\?\?\?\?'; creator = 'prvw'; extension = CFSTR(".j2c"); break; case FFSAVE_ALL: default: - type = '\?\?\?\?'; - creator = '\?\?\?\?'; + type = L'\?\?\?\?'; + creator = L'\?\?\?\?'; extension = CFSTR(""); break; } + + NavEventUPP eventUPP = NewNavEventUPP(navSetDefaultFolderProc); + mFolder = folder; // Create the dialog - error = NavCreatePutFileDialog(&mNavOptions, type, creator, NULL, NULL, &navRef); + error = NavCreatePutFileDialog(&mNavOptions, type, creator, eventUPP, (void*)this, &navRef); if (error == noErr) { CFStringRef nameString = NULL; @@ -981,7 +1034,10 @@ OSStatus LLFilePickerBase::doNavSaveDialog(ESaveFilter filter, const std::string // Run the dialog if (error == noErr) + { + PLS_FLUSH; error = NavDialogRun(navRef); + } //gViewerWindow->mWindow->afterDialog(); @@ -1033,10 +1089,12 @@ OSStatus LLFilePickerBase::doNavSaveDialog(ESaveFilter filter, const std::string } } + if (eventUPP) + DisposeNavEventUPP(eventUPP); + return error; } -// AIFIXME: Use folder bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder) { if( mLocked ) @@ -1050,7 +1108,7 @@ bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder mNavOptions.optionFlags &= ~kNavAllowMultipleFiles; { - error = doNavChooseDialog(filter); + error = doNavChooseDialog(filter, folder); } if (error == noErr) { @@ -1061,7 +1119,6 @@ bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder return success; } -// AIFIXME: Use folder bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string const& folder) { if( mLocked ) @@ -1075,7 +1132,7 @@ bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string cons mNavOptions.optionFlags |= kNavAllowMultipleFiles; { - error = doNavChooseDialog(filter); + error = doNavChooseDialog(filter, folder); } if (error == noErr) { @@ -1088,7 +1145,6 @@ bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string cons return success; } -// AIFIXME: Use folder bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filename, std::string const& folder) { if( mLocked ) @@ -1101,7 +1157,7 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena mNavOptions.optionFlags &= ~kNavAllowMultipleFiles; { - error = doNavSaveDialog(filter, filename); + error = doNavSaveDialog(filter, filename, folder); } if (error == noErr) { @@ -1203,6 +1259,7 @@ GtkWindow* LLFilePickerBase::buildFilePicker(bool is_save, bool is_folder, std:: if (!folder.empty()) { + PLS_DEBUGS << "Calling gtk_file_chooser_set_current_folder(\"" << folder << "\")." << PLS_ENDL; gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(win), folder.c_str()); @@ -1225,6 +1282,7 @@ GtkWindow* LLFilePickerBase::buildFilePicker(bool is_save, bool is_folder, std:: G_CALLBACK(LLFilePickerBase::chooser_responder), this); + PLS_FLUSH; gtk_window_set_modal(GTK_WINDOW(win), TRUE); /* GTK 2.6: if (is_folder) @@ -1383,6 +1441,7 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena } gtk_widget_show_all(GTK_WIDGET(picker)); + PLS_FLUSH; gtk_main(); rtn = (getFileCount() == 1); @@ -1430,6 +1489,7 @@ bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder gtk_window_set_title(GTK_WINDOW(picker), caption.c_str()); gtk_widget_show_all(GTK_WIDGET(picker)); + PLS_FLUSH; gtk_main(); rtn = (getFileCount() == 1); @@ -1458,6 +1518,7 @@ bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string cons gtk_window_set_title(GTK_WINDOW(picker), LLTrans::getString("load_files").c_str()); gtk_widget_show_all(GTK_WIDGET(picker)); + PLS_FLUSH; gtk_main(); rtn = !mFiles.empty(); } @@ -1485,7 +1546,6 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena return FALSE; } -// AIFIXME: Use folder bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder) { reset(); diff --git a/indra/plugins/filepicker/llfilepicker.h b/indra/plugins/filepicker/llfilepicker.h index f8e17719e..9c8db8e8a 100644 --- a/indra/plugins/filepicker/llfilepicker.h +++ b/indra/plugins/filepicker/llfilepicker.h @@ -166,6 +166,9 @@ private: OSStatus doNavChooseDialog(ELoadFilter filter); OSStatus doNavSaveDialog(ESaveFilter filter, const std::string& filename); static Boolean navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode); + static pascal void LLFilePickerBase::doNavCallbackEvent(NavEventCallbackMessage callBackSelector, NavCBRecPtr callBackParms, void* callBackUD); + ELoadFilter getLoadFilter(void) const { return mLoadFilter; } + std::string const& getFolder(void) const { return mFolder; } #endif // LL_DARWIN #if LL_GTK @@ -194,7 +197,11 @@ private: S32 mCurrentFile; bool mLocked; bool mMultiFile; - +#if LL_DARWIN + ELoadFilter mLoadFilter; + std::string mFolder; +#endif + protected: #if LL_GTK GtkWindow* buildFilePicker(bool is_save, bool is_folder, std::string const& folder);